diff --git a/README.md b/README.md
index 58d5cd2..5eb64d5 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ They are used to develop [librespot](https://github.com/plietar/librespot), an o
The `spotify-dump` tool intercepts packets by the Spotify desktop client before they are encrypted or after they are decrypted.
It works by patching the `shn_encrypt` and `shn_decrypt` routines of the client at run time.
-This currently only works on 64 bit OS X/Linux.
+##### MacOS/Linux 64 bit
To use, quit the Spotify application, and execute the `dump.sh` script with the path the spotify binary.
@@ -14,6 +14,13 @@ To use, quit the Spotify application, and execute the `dump.sh` script with the
~/spotify-analyze/dump> ./dump.sh /Applications/Spotify.app/Contents/MacOS/Spotify
```
+###### Windows 64 bit
+
+Build the solution and make sure `injector.exe` and `core.dll` are located in the same folder as `Spotify.exe`.
+
+To use, quit the Spotify application, execute the `injector.exe` and then Spotify app.
+
+
This will produce a `dump.pcap` in the current directory, which can be analyzed by the `spotify-dissect` tool.
## spotify-dump-http
diff --git a/dump-win/core/core.vcxproj b/dump-win/core/core.vcxproj
new file mode 100644
index 0000000..329d571
--- /dev/null
+++ b/dump-win/core/core.vcxproj
@@ -0,0 +1,164 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {1fb90cce-1dd3-452b-88fa-e29f616b6a24}
+ core
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;CORE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;CORE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;CORE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+
+
+ Windows
+ true
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;CORE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+ stdcpp20
+
+
+ Windows
+ true
+ true
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dump-win/core/core.vcxproj.filters b/dump-win/core/core.vcxproj.filters
new file mode 100644
index 0000000..01cf461
--- /dev/null
+++ b/dump-win/core/core.vcxproj.filters
@@ -0,0 +1,51 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/dump-win/core/core.vcxproj.user b/dump-win/core/core.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/dump-win/core/core.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/dump-win/core/dllmain.cpp b/dump-win/core/dllmain.cpp
new file mode 100644
index 0000000..11ac0bc
--- /dev/null
+++ b/dump-win/core/dllmain.cpp
@@ -0,0 +1,133 @@
+#include "pch.h"
+
+#include "pcap.h"
+#include "shn.h"
+#include "utils.h"
+
+// 48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 54 41 55 41 56 41 57 48 83
+// EC 20 8B ?? CC 00 00 00
+static const char *pattern =
+ "\x48\x89\x5c\x24\x08\x48\x89\x6c\x24\x10\x48\x89\x74\x24\x18\x57\x41\x54"
+ "\x41\x55\x41\x56\x41\x57\x48\x83\xec\x20\x8b\x00\xcc\x00\x00\x00";
+
+static const char *mask = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx?xxxx";
+
+#define DIRECTION_SEND 0
+#define DIRECTION_RECV 1
+
+static HANDLE dump_fd;
+
+typedef void(__cdecl *shn_decrypt_t)(shn_ctx *, UCHAR *, int);
+shn_decrypt_t shn_decrypt_stub = nullptr;
+
+typedef void(__cdecl *shn_encrypt_t)(shn_ctx *, UCHAR *, int);
+shn_encrypt_t shn_encrypt_stub = nullptr;
+
+int gettimeofday(struct timeval *tp, struct timezone *tzp) {
+ // note: some broken versions only have 8 trailing zero's, the correct epoch
+ // has 9 trailing zero's this magic number is the number of 100 nanosecond
+ // intervals since january 1, 1601 (utc) until 00:00:00 january 1, 1970
+ static const uint64_t epoch = ((uint64_t)116444736000000000ull);
+
+ SYSTEMTIME system_time;
+ FILETIME file_time;
+ uint64_t time;
+
+ GetSystemTime(&system_time);
+ SystemTimeToFileTime(&system_time, &file_time);
+ time = ((uint64_t)file_time.dwLowDateTime);
+ time += ((uint64_t)file_time.dwHighDateTime) << 32;
+
+ tp->tv_sec = (long)((time - epoch) / 10000000l);
+ tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
+ return 0;
+}
+
+void __cdecl shn_decrypt_proxy(shn_ctx *c, UCHAR *buf, int nBytes) {
+ shn_decrypt(c, buf, nBytes);
+
+#pragma pack(push, 1)
+ static struct {
+ uint8_t cmd;
+ uint16_t length;
+ } header = {0, 0};
+#pragma pack(pop)
+
+ if (header.cmd == 0) {
+ if (nBytes == 3)
+ memcpy(&header, buf, 3);
+ } else {
+ if (nBytes == ntohs(header.length)) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ pcap_write_packet_header(dump_fd, &tv, 4 + nBytes);
+
+ uint8_t direction = DIRECTION_RECV;
+ WriteFile(dump_fd, &direction, 1, NULL, NULL);
+ WriteFile(dump_fd, &header, 3, NULL, NULL);
+ WriteFile(dump_fd, buf, nBytes, NULL, NULL);
+ }
+
+ header.cmd = 0;
+ }
+}
+
+void __cdecl shn_encrypt_proxy(shn_ctx *c, UCHAR *buf, int nBytes) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ pcap_write_packet_header(dump_fd, &tv, 1 + nBytes);
+
+ uint8_t direction = DIRECTION_SEND;
+ WriteFile(dump_fd, &direction, 1, NULL, NULL);
+ WriteFile(dump_fd, buf, nBytes, NULL, NULL);
+
+ shn_encrypt(c, buf, nBytes);
+}
+
+DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) {
+ MODULEINFO mInfo = utils::GetModuleInfo(NULL);
+
+ auto shn_decrypt = utils::FindPattern(
+ pattern, mask, (char *)mInfo.lpBaseOfDll, mInfo.SizeOfImage);
+
+ if (shn_decrypt == nullptr) {
+ MessageBoxA(NULL, "shn_decrypt is nullptr", "error", MB_ICONERROR);
+ return 1;
+ }
+
+ char *newPos = (char *)(shn_decrypt + strlen(mask));
+
+ uintptr_t newSize = mInfo.SizeOfImage -
+ ((uintptr_t)shn_decrypt - (uintptr_t)mInfo.lpBaseOfDll) -
+ strlen(mask);
+
+ auto shn_encrypt = utils::FindPattern(pattern, mask, newPos, newSize);
+
+ if (shn_decrypt == nullptr) {
+ MessageBoxA(NULL, "shn_encrypt is nullptr", "error", MB_ICONERROR);
+ return 1;
+ }
+
+ dump_fd = CreateFileA("dump.pcap", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ pcap_write_header(dump_fd, PCAP_DLT_USER0);
+
+ shn_encrypt_stub = (shn_encrypt_t)utils::InstallHook(
+ (void *)shn_encrypt, (void *)shn_encrypt_proxy);
+
+ shn_decrypt_stub = (shn_decrypt_t)utils::InstallHook(
+ (void *)shn_decrypt, (void *)shn_decrypt_proxy);
+
+ return 0;
+};
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
+ LPVOID lpReserved) {
+
+ if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
+ CreateThread(NULL, 0, &ThreadProc, NULL, 0, NULL);
+ }
+
+ return TRUE;
+}
diff --git a/dump-win/core/framework.h b/dump-win/core/framework.h
new file mode 100644
index 0000000..5a3602b
--- /dev/null
+++ b/dump-win/core/framework.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files
+#include
diff --git a/dump-win/core/pcap.cpp b/dump-win/core/pcap.cpp
new file mode 100644
index 0000000..8f74089
--- /dev/null
+++ b/dump-win/core/pcap.cpp
@@ -0,0 +1,34 @@
+#include "pch.h"
+
+#include "pcap.h"
+
+void pcap_write_header(HANDLE hFile, uint32_t network) {
+ pcap_hdr_t hdr = {
+ .magic_number = PCAP_MAGIC,
+ .version_major = PCAP_MAJOR,
+ .version_minor = PCAP_MINOR,
+ .thiszone = 0,
+ .sigfigs = 0,
+ .snaplen = 65535,
+ .network = 12,
+ };
+
+ WriteFile(hFile, &hdr, sizeof(hdr), NULL, NULL);
+}
+
+void pcap_write_packet_header(HANDLE hFile, const struct timeval *tv,
+ uint32_t length) {
+ pcaprec_hdr_t hdr = {
+ .ts_sec = 0,
+ .ts_usec = 0,
+ .incl_len = length,
+ .orig_len = length,
+ };
+
+ if (tv != NULL) {
+ hdr.ts_sec = tv->tv_sec;
+ hdr.ts_usec = tv->tv_usec;
+ }
+
+ WriteFile(hFile, &hdr, sizeof(hdr), NULL, NULL);
+}
diff --git a/dump-win/core/pcap.h b/dump-win/core/pcap.h
new file mode 100644
index 0000000..c767735
--- /dev/null
+++ b/dump-win/core/pcap.h
@@ -0,0 +1,33 @@
+#ifndef PCAP_H
+#define PCAP_H
+
+#include "pch.h"
+
+#define PCAP_MAGIC 0xa1b2c3d4
+#define PCAP_MAJOR 2
+#define PCAP_MINOR 4
+
+#define PCAP_DLT_USER0 147
+
+typedef struct {
+ uint32_t magic_number; /* magic number */
+ uint16_t version_major; /* major version number */
+ uint16_t version_minor; /* minor version number */
+ int32_t thiszone; /* GMT to local correction */
+ uint32_t sigfigs; /* accuracy of timestamps */
+ uint32_t snaplen; /* max length of captured packets, in octets */
+ uint32_t network; /* data link type */
+} pcap_hdr_t;
+
+typedef struct {
+ uint32_t ts_sec; /* timestamp seconds */
+ uint32_t ts_usec; /* timestamp microseconds */
+ uint32_t incl_len; /* number of octets of packet saved in file */
+ uint32_t orig_len; /* actual length of packet */
+} pcaprec_hdr_t;
+
+void pcap_write_header(HANDLE hFile, uint32_t network);
+void pcap_write_packet_header(HANDLE hFile, const struct timeval *tv,
+ uint32_t length);
+
+#endif
diff --git a/dump-win/core/pch.cpp b/dump-win/core/pch.cpp
new file mode 100644
index 0000000..00df68a
--- /dev/null
+++ b/dump-win/core/pch.cpp
@@ -0,0 +1,6 @@
+// pch.cpp: source file corresponding to the pre-compiled header
+
+#include "pch.h"
+
+// When you are using pre-compiled headers, this source file is necessary for
+// compilation to succeed.
diff --git a/dump-win/core/pch.h b/dump-win/core/pch.h
new file mode 100644
index 0000000..0a34cc1
--- /dev/null
+++ b/dump-win/core/pch.h
@@ -0,0 +1,28 @@
+// pch.h: This is a precompiled header file.
+// Files listed below are compiled only once, improving build performance for
+// future builds. This also affects IntelliSense performance, including code
+// completion and many code browsing features. However, files listed here are
+// ALL re-compiled if any one of them is updated between builds. Do not add
+// files here that you will be updating frequently as this negates the
+// performance advantage.
+
+#ifndef PCH_H
+#define PCH_H
+
+// add headers that you want to pre-compile here
+#include "framework.h"
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#pragma comment(lib, "Ws2_32.lib")
+
+#endif // PCH_H
diff --git a/dump-win/core/shn.cpp b/dump-win/core/shn.cpp
new file mode 100644
index 0000000..4ffe3d5
--- /dev/null
+++ b/dump-win/core/shn.cpp
@@ -0,0 +1,467 @@
+#include "pch.h"
+
+/* $Id: shn.c 182 2009-03-12 08:21:53Z zagor $ */
+/* Shannon: Shannon stream cipher and MAC -- reference implementation */
+
+/*
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND AGAINST
+INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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.
+*/
+
+/* interface */
+#include
+#include
+#include "shn.h"
+
+/*
+ * FOLD is how many register cycles need to be performed after combining the
+ * last byte of key and non-linear feedback, before every byte depends on every
+ * byte of the key. This depends on the feedback and nonlinear functions, and
+ * on where they are combined into the register. Making it same as the
+ * register length is a safe and conservative choice.
+ */
+#define FOLD N /* how many iterations of folding to do */
+#define INITKONST 0x6996c53a /* value of KONST to use during key loading */
+#define KEYP 13 /* where to insert key/MAC/counter words */
+
+/* some useful macros -- machine independent little-endian */
+#define Byte(x,i) ((UCHAR)(((x) >> (8*(i))) & 0xFF))
+#define BYTE2WORD(b) ( \
+ (((WORD)(b)[3] & 0xFF)<<24) | \
+ (((WORD)(b)[2] & 0xFF)<<16) | \
+ (((WORD)(b)[1] & 0xFF)<<8) | \
+ (((WORD)(b)[0] & 0xFF)) \
+)
+#define WORD2BYTE(w, b) { \
+ (b)[3] = Byte(w,3); \
+ (b)[2] = Byte(w,2); \
+ (b)[1] = Byte(w,1); \
+ (b)[0] = Byte(w,0); \
+}
+#define XORWORD(w, b) { \
+ (b)[3] ^= Byte(w,3); \
+ (b)[2] ^= Byte(w,2); \
+ (b)[1] ^= Byte(w,1); \
+ (b)[0] ^= Byte(w,0); \
+}
+
+/* Nonlinear transform (sbox) of a word.
+ * There are two slightly different combinations.
+ */
+static WORD sbox1 (WORD w)
+{
+ w ^= ROTL (w, 5) | ROTL (w, 7);
+ w ^= ROTL (w, 19) | ROTL (w, 22);
+ return w;
+}
+
+static WORD sbox2 (WORD w)
+{
+ w ^= ROTL (w, 7) | ROTL (w, 22);
+ w ^= ROTL (w, 5) | ROTL (w, 19);
+ return w;
+}
+
+/* cycle the contents of the register and calculate output word in c->sbuf.
+ */
+static void cycle (shn_ctx * c)
+{
+ WORD t;
+ int i;
+
+ /* nonlinear feedback function */
+ t = c->R[12] ^ c->R[13] ^ c->konst;
+ t = sbox1 (t) ^ ROTL (c->R[0], 1);
+ /* shift register */
+ for (i = 1; i < N; ++i)
+ c->R[i - 1] = c->R[i];
+ c->R[N - 1] = t;
+ t = sbox2 (c->R[2] ^ c->R[15]);
+ c->R[0] ^= t;
+ c->sbuf = t ^ c->R[8] ^ c->R[12];
+}
+
+/* The Shannon MAC function is modelled after the concepts of Phelix and SHA.
+ * Basically, words to be accumulated in the MAC are incorporated in two
+ * different ways:
+ * 1. They are incorporated into the stream cipher register at a place
+ * where they will immediately have a nonlinear effect on the state
+ * 2. They are incorporated into bit-parallel CRC-16 registers; the
+ * contents of these registers will be used in MAC finalization.
+ */
+
+/* Accumulate a CRC of input words, later to be fed into MAC.
+ * This is actually 32 parallel CRC-16s, using the IBM CRC-16
+ * polynomial x^16 + x^15 + x^2 + 1.
+ */
+static void crcfunc (shn_ctx * c, WORD i)
+{
+ WORD t;
+ int j;
+
+ /* Accumulate CRC of input */
+ t = c->CRC[0] ^ c->CRC[2] ^ c->CRC[15] ^ i;
+ for (j = 1; j < N; ++j)
+ c->CRC[j - 1] = c->CRC[j];
+ c->CRC[N - 1] = t;
+}
+
+/* Normal MAC word processing: do both stream register and CRC.
+ */
+static void macfunc (shn_ctx * c, WORD i)
+{
+ crcfunc (c, i);
+ c->R[KEYP] ^= i;
+}
+
+/* initialise to known state
+ */
+static void shn_initstate (shn_ctx * c)
+{
+ int i;
+
+ /* Register initialised to Fibonacci numbers; Counter zeroed. */
+ c->R[0] = 1;
+ c->R[1] = 1;
+ for (i = 2; i < N; ++i)
+ c->R[i] = c->R[i - 1] + c->R[i - 2];
+ c->konst = INITKONST;
+}
+
+/* Save the current register state
+ */
+static void shn_savestate (shn_ctx * c)
+{
+ int i;
+
+ for (i = 0; i < N; ++i)
+ c->initR[i] = c->R[i];
+}
+
+/* initialise to previously saved register state
+ */
+static void shn_reloadstate (shn_ctx * c)
+{
+ int i;
+
+ for (i = 0; i < N; ++i)
+ c->R[i] = c->initR[i];
+}
+
+/* Initialise "konst"
+ */
+static void shn_genkonst (shn_ctx * c)
+{
+ c->konst = c->R[0];
+}
+
+/* Load key material into the register
+ */
+#define ADDKEY(k) \
+ c->R[KEYP] ^= (k);
+
+/* extra nonlinear diffusion of register for key and MAC */
+static void shn_diffuse (shn_ctx * c)
+{
+ int i;
+
+ for (i = 0; i < FOLD; ++i)
+ cycle (c);
+}
+
+/* Common actions for loading key material
+ * Allow non-word-multiple key and nonce material.
+ * Note also initializes the CRC register as a side effect.
+ */
+static void shn_loadkey (shn_ctx * c, const UCHAR *key, int keylen)
+{
+ int i, j;
+ WORD k;
+ UCHAR xtra[4];
+
+ /* start folding in key */
+ for (i = 0; i < (keylen & ~0x3); i += 4) {
+ k = BYTE2WORD (&key[i]);
+ ADDKEY (k);
+ cycle (c);
+ }
+
+ /* if there were any extra key bytes, zero pad to a word */
+ if (i < keylen) {
+ for (j = 0 /* i unchanged */ ; i < keylen; ++i)
+ xtra[j++] = key[i];
+ for ( /* j unchanged */ ; j < 4; ++j)
+ xtra[j] = 0;
+ k = BYTE2WORD (xtra);
+ ADDKEY (k);
+ cycle (c);
+ }
+
+ /* also fold in the length of the key */
+ ADDKEY (keylen);
+ cycle (c);
+
+ /* save a copy of the register */
+ for (i = 0; i < N; ++i)
+ c->CRC[i] = c->R[i];
+
+ /* now diffuse */
+ shn_diffuse (c);
+
+ /* now xor the copy back -- makes key loading irreversible */
+ for (i = 0; i < N; ++i)
+ c->R[i] ^= c->CRC[i];
+}
+
+/* Published "key" interface
+ */
+void shn_key (shn_ctx * c, const UCHAR *key, int keylen)
+{
+ shn_initstate (c);
+ shn_loadkey (c, key, keylen);
+ shn_genkonst (c); /* in case we proceed to stream generation */
+ shn_savestate (c);
+ c->nbuf = 0;
+}
+
+/* Published "IV" interface
+ */
+void shn_nonce (shn_ctx * c, const UCHAR *nonce, int noncelen)
+{
+ shn_reloadstate (c);
+ c->konst = INITKONST;
+ shn_loadkey (c, nonce, noncelen);
+ shn_genkonst (c);
+ c->nbuf = 0;
+}
+
+/* XOR pseudo-random bytes into buffer
+ * Note: doesn't play well with MAC functions.
+ */
+void shn_stream (shn_ctx * c, UCHAR * buf, int nbytes)
+{
+ UCHAR *endbuf;
+
+ /* handle any previously buffered bytes */
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf++ ^= c->sbuf & 0xFF;
+ c->sbuf >>= 8;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+
+ /* handle whole words */
+ endbuf = &buf[nbytes & ~((WORD) 0x03)];
+ while (buf < endbuf) {
+ cycle (c);
+ XORWORD (c->sbuf, buf);
+ buf += 4;
+ }
+
+ /* handle any trailing bytes */
+ nbytes &= 0x03;
+ if (nbytes != 0) {
+ cycle (c);
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf++ ^= c->sbuf & 0xFF;
+ c->sbuf >>= 8;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* accumulate words into MAC without encryption
+ * Note that plaintext is accumulated for MAC.
+ */
+void shn_maconly (shn_ctx * c, const UCHAR * buf, int nbytes)
+{
+ UCHAR *endbuf;
+
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ while (c->nbuf != 0 && nbytes != 0) {
+ c->mbuf ^= (*buf++) << (32 - c->nbuf);
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ if (c->nbuf != 0) /* not a whole word yet */
+ return;
+ /* LFSR already cycled */
+ macfunc (c, c->mbuf);
+ }
+
+ /* handle whole words */
+ endbuf = (unsigned char*)&buf[nbytes & ~((WORD) 0x03)];
+ while (buf < endbuf) {
+ cycle (c);
+ macfunc (c, BYTE2WORD (buf));
+ buf += 4;
+ }
+
+ /* handle any trailing bytes */
+ nbytes &= 0x03;
+ if (nbytes != 0) {
+ cycle (c);
+ c->mbuf = 0;
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ c->mbuf ^= (*buf++) << (32 - c->nbuf);
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* Combined MAC and encryption.
+ * Note that plaintext is accumulated for MAC.
+ */
+void shn_encrypt (shn_ctx * c, UCHAR * buf, int nbytes)
+{
+ UCHAR *endbuf;
+ WORD t = 0;
+
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ while (c->nbuf != 0 && nbytes != 0) {
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ if (c->nbuf != 0) /* not a whole word yet */
+ return;
+ /* LFSR already cycled */
+ macfunc (c, c->mbuf);
+ }
+
+ /* handle whole words */
+ endbuf = &buf[nbytes & ~((WORD) 0x03)];
+ while (buf < endbuf) {
+ cycle (c);
+ t = BYTE2WORD (buf);
+ macfunc (c, t);
+ t ^= c->sbuf;
+ WORD2BYTE (t, buf);
+ buf += 4;
+ }
+
+ /* handle any trailing bytes */
+ nbytes &= 0x03;
+ if (nbytes != 0) {
+ cycle (c);
+ c->mbuf = 0;
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* Combined MAC and decryption.
+ * Note that plaintext is accumulated for MAC.
+ */
+void shn_decrypt (shn_ctx * c, UCHAR * buf, int nbytes)
+{
+ UCHAR *endbuf;
+ WORD t = 0;
+
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ if (c->nbuf != 0) /* not a whole word yet */
+ return;
+ /* LFSR already cycled */
+ macfunc (c, c->mbuf);
+ }
+
+ /* handle whole words */
+ endbuf = &buf[nbytes & ~((WORD) 0x03)];
+ while (buf < endbuf) {
+ cycle (c);
+ t = BYTE2WORD (buf) ^ c->sbuf;
+ macfunc (c, t);
+ WORD2BYTE (t, buf);
+ buf += 4;
+ }
+
+ /* handle any trailing bytes */
+ nbytes &= 0x03;
+ if (nbytes != 0) {
+ cycle (c);
+ c->mbuf = 0;
+ c->nbuf = 32;
+ while (c->nbuf != 0 && nbytes != 0) {
+ *buf ^= (c->sbuf >> (32 - c->nbuf)) & 0xFF;
+ c->mbuf ^= *buf << (32 - c->nbuf);
+ ++buf;
+ c->nbuf -= 8;
+ --nbytes;
+ }
+ }
+}
+
+/* Having accumulated a MAC, finish processing and return it.
+ * Note that any unprocessed bytes are treated as if
+ * they were encrypted zero bytes, so plaintext (zero) is accumulated.
+ */
+void shn_finish (shn_ctx * c, UCHAR * buf, int nbytes)
+{
+ int i;
+
+ /* handle any previously buffered bytes */
+ if (c->nbuf != 0) {
+ /* LFSR already cycled */
+ macfunc (c, c->mbuf);
+ }
+
+ /* perturb the MAC to mark end of input.
+ * Note that only the stream register is updated, not the CRC. This is an
+ * action that can't be duplicated by passing in plaintext, hence
+ * defeating any kind of extension attack.
+ */
+ cycle (c);
+ ADDKEY (INITKONST ^ (c->nbuf << 3));
+ c->nbuf = 0;
+
+ /* now add the CRC to the stream register and diffuse it */
+ for (i = 0; i < N; ++i)
+ c->R[i] ^= c->CRC[i];
+ shn_diffuse (c);
+
+ /* produce output from the stream buffer */
+ while (nbytes > 0) {
+ cycle (c);
+ if (nbytes >= 4) {
+ WORD2BYTE (c->sbuf, buf);
+ nbytes -= 4;
+ buf += 4;
+ }
+ else {
+ for (i = 0; i < nbytes; ++i)
+ buf[i] = Byte (c->sbuf, i);
+ break;
+ }
+ }
+}
diff --git a/dump-win/core/shn.h b/dump-win/core/shn.h
new file mode 100644
index 0000000..1233634
--- /dev/null
+++ b/dump-win/core/shn.h
@@ -0,0 +1,69 @@
+/* $Id: shn.h 182 2009-03-12 08:21:53Z zagor $ */
+/* Shannon: Shannon stream cipher and MAC header files */
+
+/*
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND AGAINST
+INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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.
+*/
+
+#ifndef _SHN_DEFINED
+#define _SHN_DEFINED 1
+
+#include
+
+#if __STDC_VERSION__ >= 199901
+#include
+#endif
+
+#define N 16
+#define WORDSIZE 32
+#define UCHAR unsigned char
+
+#if __STDC_VERSION__ >= 199901
+#define WORD uint32_t
+#define WORD_MAX UINT32_MAX
+#elif UINT_MAX >= 0xffffffff
+#define WORD unsigned int
+#define WORD_MAX UINT_MAX
+#else
+#define WORD unsigned long
+#define WORD_MAX ULONG_MAX
+#endif
+
+#if WORD_MAX == 0xffffffff
+#define ROTL(w,x) (((w) << (x))|((w) >> (32 - (x))))
+#define ROTR(w,x) (((w) >> (x))|((w) << (32 - (x))))
+#else
+#define ROTL(w,x) (((w) << (x))|(((w) & 0xffffffff) >> (32 - (x))))
+#define ROTR(w,x) ((((w) & 0xffffffff) >> (x))|((w) << (32 - (x))))
+#endif
+
+typedef struct
+{
+ WORD R[N]; /* Working storage for the shift register */
+ WORD CRC[N]; /* Working storage for CRC accumulation */
+ WORD initR[N]; /* saved register contents */
+ WORD konst; /* key dependent semi-constant */
+ WORD sbuf; /* encryption buffer */
+ WORD mbuf; /* partial word MAC buffer */
+ int nbuf; /* number of part-word stream bits buffered */
+} shn_ctx;
+
+/* interface definitions */
+void shn_key (shn_ctx * c, const UCHAR *key, int keylen); /* set key */
+void shn_nonce (shn_ctx * c, const UCHAR *nonce, int nlen); /* set Init Vector */
+void shn_stream (shn_ctx * c, UCHAR * buf, int nbytes); /* stream cipher */
+void shn_maconly (shn_ctx * c, const UCHAR * buf, int nbytes); /* accumulate MAC */
+void shn_encrypt (shn_ctx * c, UCHAR * buf, int nbytes); /* encrypt + MAC */
+void shn_decrypt (shn_ctx * c, UCHAR * buf, int nbytes); /* decrypt + MAC */
+void shn_finish (shn_ctx * c, UCHAR * buf, int nbytes); /* finalise MAC */
+#endif /* _SHN_DEFINED */
diff --git a/dump-win/core/utils.cpp b/dump-win/core/utils.cpp
new file mode 100644
index 0000000..a2e9a71
--- /dev/null
+++ b/dump-win/core/utils.cpp
@@ -0,0 +1,138 @@
+#include "pch.h"
+
+#include "utils.h"
+
+void *utils::AllocatePageNearAddress(void *targetAddr) {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+ const uint64_t PAGE_SIZE = sysInfo.dwPageSize;
+
+ uint64_t startAddr =
+ (uint64_t(targetAddr) &
+ ~(PAGE_SIZE - 1)); // round down to nearest page boundary
+ uint64_t minAddr = min(startAddr - 0x7FFFFF00,
+ (uint64_t)sysInfo.lpMinimumApplicationAddress);
+ uint64_t maxAddr = max(startAddr + 0x7FFFFF00,
+ (uint64_t)sysInfo.lpMaximumApplicationAddress);
+
+ uint64_t startPage = (startAddr - (startAddr % PAGE_SIZE));
+
+ uint64_t pageOffset = 1;
+ while (1) {
+ uint64_t byteOffset = pageOffset * PAGE_SIZE;
+ uint64_t highAddr = startPage + byteOffset;
+ uint64_t lowAddr = (startPage > byteOffset) ? startPage - byteOffset : 0;
+
+ bool needsExit = highAddr > maxAddr && lowAddr < minAddr;
+
+ if (highAddr < maxAddr) {
+ void *outAddr =
+ VirtualAlloc((void *)highAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE);
+ if (outAddr)
+ return outAddr;
+ }
+
+ if (lowAddr > minAddr) {
+ void *outAddr =
+ VirtualAlloc((void *)lowAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE);
+ if (outAddr != nullptr)
+ return outAddr;
+ }
+
+ pageOffset++;
+
+ if (needsExit) {
+ break;
+ }
+ }
+
+ return nullptr;
+}
+
+void utils::WriteAbsoluteJump64(void *absJumpMemory, void *addrToJumpTo) {
+ uint8_t absJumpInstructions[] = {
+ 0x49, 0xBA, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, addr
+ 0x41, 0xFF, 0xE2 // jmp r10
+ };
+
+ uint64_t addrToJumpTo64 = (uint64_t)addrToJumpTo;
+ memcpy(&absJumpInstructions[2], &addrToJumpTo64, sizeof(addrToJumpTo64));
+ memcpy(absJumpMemory, absJumpInstructions, sizeof(absJumpInstructions));
+}
+
+void *utils::InstallHook(void *func2hook, void *payloadFunction) {
+ // Store the original function's bytes
+ uint8_t originalBytes[5];
+ memcpy(originalBytes, func2hook, 5);
+
+ // Store the original function pointer
+ void *originalFunction = func2hook;
+
+ void *relayFuncMemory = AllocatePageNearAddress(func2hook);
+ WriteAbsoluteJump64(relayFuncMemory,
+ payloadFunction); // write relay func instructions
+
+ // now that the relay function is built, we need to install the E9 jump into
+ // the target func, this will jump to the relay function
+ DWORD oldProtect;
+ VirtualProtect(func2hook, 1024, PAGE_EXECUTE_READWRITE, &oldProtect);
+
+ // 32 bit relative jump opcode is E9, takes 1 32 bit operand for jump offset
+ uint8_t jmpInstruction[5] = {0xE9, 0x0, 0x0, 0x0, 0x0};
+
+ // to fill out the last 4 bytes of jmpInstruction, we need the offset between
+ // the relay function and the instruction immediately AFTER the jmp
+ // instruction
+ const uint64_t relAddr = (uint64_t)relayFuncMemory -
+ ((uint64_t)func2hook + sizeof(jmpInstruction));
+ memcpy(jmpInstruction + 1, &relAddr, 4);
+
+ // install the hook
+ memcpy(func2hook, jmpInstruction, sizeof(jmpInstruction));
+
+ // Return the original function pointer
+ return originalFunction;
+}
+
+const char *utils::FindPattern(const char *pattern, const char *mask,
+ const char *begin, size_t size) {
+ if (!pattern || !mask || !begin) {
+ return nullptr;
+ }
+
+ size_t patternLen = strlen(mask);
+ if (patternLen > size) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i <= size - patternLen; i++) {
+ bool found = true;
+
+ for (size_t j = 0; j < patternLen; j++) {
+ if (mask[j] != '?' && pattern[j] != *(begin + i + j)) {
+ found = false;
+ break;
+ }
+ }
+
+ if (found) {
+ return begin + i;
+ }
+ }
+
+ return nullptr;
+}
+
+MODULEINFO utils::GetModuleInfo(char *szModule) {
+ MODULEINFO mInfo = {0};
+ HMODULE hModule = GetModuleHandleA(szModule);
+ if (hModule != 0) {
+ GetModuleInformation(GetCurrentProcess(), hModule, &mInfo,
+ sizeof(MODULEINFO));
+ }
+
+ return mInfo;
+}
diff --git a/dump-win/core/utils.h b/dump-win/core/utils.h
new file mode 100644
index 0000000..b81b284
--- /dev/null
+++ b/dump-win/core/utils.h
@@ -0,0 +1,13 @@
+#pragma once
+#include "pch.h"
+
+class utils {
+public:
+ static void *AllocatePageNearAddress(void *targetAddr);
+ static void WriteAbsoluteJump64(void *absJumpMemory, void *addrToJumpTo);
+ static void *InstallHook(void *func2hook, void *payloadFunction);
+
+ static MODULEINFO GetModuleInfo(char *szModule);
+ static const char *FindPattern(const char *pattern, const char *mask,
+ const char *begin, size_t size);
+};
diff --git a/dump-win/dump-win.sln b/dump-win/dump-win.sln
new file mode 100644
index 0000000..e8e8c25
--- /dev/null
+++ b/dump-win/dump-win.sln
@@ -0,0 +1,41 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32505.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "core\core.vcxproj", "{1FB90CCE-1DD3-452B-88FA-E29F616B6A24}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injector", "injector\injector.vcxproj", "{991E683A-E927-4426-90F7-8C599A96F6CC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Debug|x64.ActiveCfg = Debug|x64
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Debug|x64.Build.0 = Debug|x64
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Debug|x86.ActiveCfg = Debug|Win32
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Debug|x86.Build.0 = Debug|Win32
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Release|x64.ActiveCfg = Release|x64
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Release|x64.Build.0 = Release|x64
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Release|x86.ActiveCfg = Release|Win32
+ {1FB90CCE-1DD3-452B-88FA-E29F616B6A24}.Release|x86.Build.0 = Release|Win32
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Debug|x64.ActiveCfg = Debug|x64
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Debug|x64.Build.0 = Debug|x64
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Debug|x86.ActiveCfg = Debug|Win32
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Debug|x86.Build.0 = Debug|Win32
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Release|x64.ActiveCfg = Release|x64
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Release|x64.Build.0 = Release|x64
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Release|x86.ActiveCfg = Release|Win32
+ {991E683A-E927-4426-90F7-8C599A96F6CC}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0A1C2237-B2F3-4ABF-853F-AAA156A3CC4D}
+ EndGlobalSection
+EndGlobal
diff --git a/dump-win/injector/injector.cpp b/dump-win/injector/injector.cpp
new file mode 100644
index 0000000..2efdc86
--- /dev/null
+++ b/dump-win/injector/injector.cpp
@@ -0,0 +1,67 @@
+#include
+
+#include
+
+#include
+
+static const char *DLL_FILE = "core.dll";
+static const wchar_t *TARGET = L"Spotify.exe";
+
+DWORD GetProcId(const wchar_t *procName) {
+ DWORD procId = 0;
+ HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+ if (hSnap != INVALID_HANDLE_VALUE) {
+ PROCESSENTRY32 procEntry;
+ procEntry.dwSize = sizeof(procEntry);
+
+ if (Process32First(hSnap, &procEntry)) {
+ do {
+ if (!wcscmp(procEntry.szExeFile, procName)) {
+ procId = procEntry.th32ProcessID;
+ break;
+ }
+ } while (Process32Next(hSnap, &procEntry));
+ }
+ }
+
+ CloseHandle(hSnap);
+ return procId;
+}
+
+int main() {
+ std::wcout << "Attempting to inject \"" << DLL_FILE << "\" into \""
+ << std::wstring(TARGET) << "\"..." << std::endl;
+
+ DWORD procId = 0;
+ while (!procId) {
+ procId = GetProcId(TARGET);
+ Sleep(50);
+ }
+
+ // Possibly injecting too fast into Spotify.exe resulting in an error, so
+ // delay?
+ Sleep(50);
+
+ HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, procId);
+
+ if (hProcess && hProcess != INVALID_HANDLE_VALUE) {
+ void *loc = VirtualAllocEx(hProcess, 0, MAX_PATH, MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE);
+
+ WriteProcessMemory(hProcess, loc, DLL_FILE, strlen(DLL_FILE) + 1, 0);
+
+ HANDLE hThread = CreateRemoteThread(
+ hProcess, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, loc, 0, 0);
+
+ if (hThread) {
+ CloseHandle(hThread);
+ }
+ }
+
+ if (hProcess) {
+ CloseHandle(hProcess);
+ }
+
+ std::cout << "Done" << std::endl;
+}
diff --git a/dump-win/injector/injector.vcxproj b/dump-win/injector/injector.vcxproj
new file mode 100644
index 0000000..cd4b42a
--- /dev/null
+++ b/dump-win/injector/injector.vcxproj
@@ -0,0 +1,135 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {991e683a-e927-4426-90f7-8c599a96f6cc}
+ injector
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dump-win/injector/injector.vcxproj.filters b/dump-win/injector/injector.vcxproj.filters
new file mode 100644
index 0000000..9ce493a
--- /dev/null
+++ b/dump-win/injector/injector.vcxproj.filters
@@ -0,0 +1,22 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/dump-win/injector/injector.vcxproj.user b/dump-win/injector/injector.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/dump-win/injector/injector.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file