diff --git a/G-WinMem/G-WinMem/G-WinMem.cpp b/G-WinMem/G-WinMem/G-WinMem.cpp
index 826e0b0..52b1e10 100644
Binary files a/G-WinMem/G-WinMem/G-WinMem.cpp and b/G-WinMem/G-WinMem/G-WinMem.cpp differ
diff --git a/G-WinMem/G-WinMem/G-WinMem.vcxproj b/G-WinMem/G-WinMem/G-WinMem.vcxproj
index 6285c9b..58e6a36 100644
--- a/G-WinMem/G-WinMem/G-WinMem.vcxproj
+++ b/G-WinMem/G-WinMem/G-WinMem.vcxproj
@@ -98,7 +98,7 @@
- Use
+ NotUsing
Level3
Disabled
true
@@ -148,11 +148,15 @@
+
+
+
+
Create
Create
@@ -161,6 +165,5 @@
-
-
+
\ No newline at end of file
diff --git a/G-WinMem/G-WinMem/G-WinMem.vcxproj.filters b/G-WinMem/G-WinMem/G-WinMem.vcxproj.filters
index 65bd3c3..ebe7bb6 100644
--- a/G-WinMem/G-WinMem/G-WinMem.vcxproj.filters
+++ b/G-WinMem/G-WinMem/G-WinMem.vcxproj.filters
@@ -21,6 +21,15 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
@@ -29,5 +38,8 @@
Source Files
+
+ Source Files
+
\ No newline at end of file
diff --git a/G-WinMem/G-WinMem/Process.cpp b/G-WinMem/G-WinMem/Process.cpp
new file mode 100644
index 0000000..e8046d4
--- /dev/null
+++ b/G-WinMem/G-WinMem/Process.cpp
@@ -0,0 +1,263 @@
+
+#include "ctpl_stl.h"
+#include "Process.h"
+
+#include
+#include
+
+Process::Process() : Process(0)
+{}
+
+Process::Process(int pid)
+ : mPid(pid),
+ mHandle(nullptr)
+{}
+
+bool Process::Open()
+{
+ mHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_OPERATION, false, mPid);
+
+ return true;
+}
+
+std::vector Process::GetChunks()
+{
+ return mChunks;
+}
+
+void Process::Close()
+{
+ CloseHandle(mHandle);
+}
+
+void Process::PrintCachedResults(std::vector cache)
+{
+ const auto offset = 4;
+ static std::mutex m;
+ Open();
+
+ for (auto addr : cache) {
+ u_char rawMem[1024] = { 0 };
+
+ if (!ReadProcessMemory(mHandle, addr, rawMem, 1024, nullptr))
+ {
+ std::cout << "Failed to read memory at " << addr << std::endl;
+ return;
+ }
+
+ for (auto i = 0; i < (1024 - ((256 - 1) * offset)); i += offset)
+ {
+ unsigned char wannabeRC4data[1024] = { 0 };
+ unsigned char data[256] = { 0 };
+ memcpy(wannabeRC4data, rawMem + i, 1024);
+
+ auto isvalid = true;
+
+ for (auto j = 0; j < 1024; j++)
+ {
+ if (j % 4 != 0 && wannabeRC4data[j] != 0)
+ {
+ isvalid = false;
+ break;
+ }
+ if (j % 4 == 0)
+ {
+ data[j / 4] = wannabeRC4data[j];
+ }
+ }
+ if (isvalid)
+ {
+ m.lock();
+ for (auto idx : data)
+ printf("%02X", static_cast(idx) & 0xFF);
+
+ std::cout << std::endl;
+ m.unlock();
+ }
+ }
+ }
+ Close();
+}
+
+void Process::PrintRC4Possibilities()
+{
+ SYSTEM_INFO sys_info;
+
+ static std::mutex m;
+
+ GetSystemInfo(&sys_info);
+
+ Open();
+
+ FindMaps(sys_info);
+
+ const auto offset = 4;
+
+ CreateMapsForRC4();
+
+ for (auto k = 0; k < mRC4Maps.size(); k++)
+ {
+ auto mem = mRC4Maps[k];
+
+ if (mem->mSize >= 1024 && mem->mSize <= 1024 + 2 * offset)
+ {
+ for (auto i = 0; i < (mem->mSize - ((256 - 1) * offset)); i += offset)
+ {
+ unsigned char wannabeRC4data[1024] = { 0 };
+ unsigned char data[256] = { 0 };
+ memcpy(wannabeRC4data, static_cast(mem->mStart) + i, 1024);
+
+ auto isvalid = true;
+
+ for (auto j = 0; j < 1024; j++)
+ {
+ if (j % 4 != 0 && wannabeRC4data[j] != 0)
+ {
+ isvalid = false;
+ break;
+ }
+ if (j % 4 == 0)
+ {
+ data[j / 4] = wannabeRC4data[j];
+ }
+ }
+ if (isvalid)
+ {
+ m.lock();
+ printf("%llx\n",reinterpret_cast(mOutCache[k]));
+ for (auto idx : data)
+ printf("%02X", static_cast(idx) & 0xFF);
+
+ std::cout << std::endl;
+ m.unlock();
+ }
+ }
+ }
+ delete mem;
+ }
+ Close();
+}
+
+void Process::CreateMapFromChunk(MemoryChunk *chunk)
+{
+ const auto offset = 4;
+ const auto dump = new unsigned char[chunk->mSize + 1];
+
+ memset(dump, 0, chunk->mSize + 1);
+
+ if (!ReadProcessMemory(mHandle, chunk->mStart, dump, chunk->mSize, nullptr))
+ {
+ std::cout << "Failed to read memory at: " << chunk->mStart << std::endl;
+ return;
+ }
+
+ auto maskCount = 0;
+ int nToMap[256] = { 0 };
+ int removeMap[256] = { 0 };
+
+ for (auto i = 0; i < 256; i++) {
+ nToMap[i] = -1;
+ removeMap[i] = -1;
+ }
+
+ auto matchStart = -1;
+ auto matchEnd = -1;
+
+ for (auto i = 0; i < chunk->mSize; i += offset)
+ {
+ const auto b = (static_cast(dump[i]) + 128) % 256;
+ const auto indInMap = (i / 4) % 256;
+
+ const auto deletedNumber = removeMap[indInMap];
+
+ if (deletedNumber != -1)
+ {
+ nToMap[deletedNumber] = -1;
+ maskCount--;
+ removeMap[indInMap] = -1;
+ }
+
+ if (nToMap[b] == -1)
+ {
+ maskCount++;
+ removeMap[indInMap] = b;
+ nToMap[b] = indInMap;
+ }
+ else
+ {
+ removeMap[nToMap[b]] = -1;
+ removeMap[indInMap] = b;
+ nToMap[b] = indInMap;
+ }
+
+ if (maskCount == 256)
+ {
+ if (matchStart == -1)
+ {
+ matchStart = i - ((256 - 1) * offset);
+ matchEnd = i;
+ }
+
+ if (matchEnd < i - ((256 - 1) * offset))
+ {
+ //printf("maybeValid -> %p\n", static_cast(chunk->mStart) + matchStart);
+ mOutCache.push_back(static_cast(chunk->mStart) + matchStart);
+ mRC4Maps.push_back(new MemoryChunk(dump + matchStart, matchEnd - matchStart + 4));
+
+ matchStart = i - ((256 - 1) * offset);
+ }
+ matchEnd = i;
+ }
+ }
+ if (matchStart != -1)
+ {
+ mOutCache.push_back(static_cast(chunk->mStart) + matchStart);
+ mRC4Maps.push_back(new MemoryChunk(dump + matchStart, matchEnd - matchStart + 4));
+ }
+ delete chunk;
+}
+
+void Process::CreateMapsForRC4()
+{
+ ctpl::thread_pool p(5);
+
+ for (auto chunk : mChunks) {
+ p.push(std::bind(&Process::CreateMapFromChunk, this, chunk));
+ }
+
+ p.stop(true);
+}
+
+
+
+void Process::FindMaps(SYSTEM_INFO sys_info)
+{
+
+ auto addr = reinterpret_cast(sys_info.lpMinimumApplicationAddress);
+ const auto end = reinterpret_cast(sys_info.lpMaximumApplicationAddress);
+
+ MEMORY_BASIC_INFORMATION mbi;
+
+ while (addr < end) {
+ if (!VirtualQueryEx(mHandle, reinterpret_cast(addr), &mbi, sizeof(mbi))) {
+ std::cout << "Failed to get memory maps\n";
+ return;
+ }
+
+ if (mbi.State == MEM_COMMIT && ((mbi.Protect & PAGE_GUARD) == 0) && ((mbi.Protect & PAGE_NOACCESS) == 0)) {
+ mChunks.push_back(new MemoryChunk(reinterpret_cast(addr), mbi.RegionSize));
+ }
+ addr += mbi.RegionSize;
+ }
+}
+
+
+
+Process::~Process()
+{
+ for (auto m : mChunks)
+ delete m;
+
+ for (auto m : mRC4Maps)
+ delete m;
+}
diff --git a/G-WinMem/G-WinMem/Process.h b/G-WinMem/G-WinMem/Process.h
new file mode 100644
index 0000000..a12ff06
--- /dev/null
+++ b/G-WinMem/G-WinMem/Process.h
@@ -0,0 +1,40 @@
+#pragma once
+#include
+#include
+
+class MemoryChunk
+{
+public:
+ MemoryChunk(LPVOID start, SIZE_T size);
+ LPVOID mStart;
+ SIZE_T mSize;
+};
+
+inline MemoryChunk::MemoryChunk(LPVOID start, SIZE_T size) :
+ mStart(start),
+ mSize(size)
+{}
+
+
+class Process
+{
+public:
+ Process();
+ Process(int pid);
+ bool Open();
+ void Close();
+ void FindMaps(SYSTEM_INFO sys_info);
+ void CreateMapsForRC4();
+ void CreateMapFromChunk(MemoryChunk *chunk);
+ void PrintRC4Possibilities();
+ void PrintCachedResults(std::vector cache);
+ ~Process();
+ std::vector GetChunks();
+private:
+ int mPid;
+ HANDLE mHandle;
+ std::vector mChunks;
+ std::vector mRC4Maps;
+ std::vector mOutCache;
+};
+
diff --git a/G-WinMem/G-WinMem/ctpl_stl.h b/G-WinMem/G-WinMem/ctpl_stl.h
new file mode 100644
index 0000000..9b59bcd
--- /dev/null
+++ b/G-WinMem/G-WinMem/ctpl_stl.h
@@ -0,0 +1,256 @@
+/*********************************************************
+*
+* Copyright (C) 2014 by Vitaliy Vitsentiy
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*********************************************************/
+
+
+#ifndef __ctpl_stl_thread_pool_H__
+#define __ctpl_stl_thread_pool_H__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+
+// thread pool to run user's functors with signature
+// ret func(int id, other_params)
+// where id is the index of the thread that runs the functor
+// ret is some return type
+
+
+namespace ctpl {
+
+ namespace detail {
+ template
+ class Queue {
+ public:
+ bool push(T const & value) {
+ std::unique_lock lock(this->mutex);
+ this->q.push(value);
+ return true;
+ }
+ // deletes the retrieved element, do not use for non integral types
+ bool pop(T & v) {
+ std::unique_lock lock(this->mutex);
+ if (this->q.empty())
+ return false;
+ v = this->q.front();
+ this->q.pop();
+ return true;
+ }
+ bool empty() {
+ std::unique_lock lock(this->mutex);
+ return this->q.empty();
+ }
+ private:
+ std::queue q;
+ std::mutex mutex;
+ };
+ }
+
+ class thread_pool {
+
+ public:
+
+ thread_pool() { this->init(); }
+ thread_pool(int nThreads) { this->init(); this->resize(nThreads); }
+
+ // the destructor waits for all the functions in the queue to be finished
+ ~thread_pool() {
+ this->stop(true);
+ }
+
+ // get the number of running threads in the pool
+ int size() { return static_cast(this->threads.size()); }
+
+ // number of idle threads
+ int n_idle() { return this->nWaiting; }
+ int n_pending() { return this->nPending; }
+ std::thread & get_thread(int i) { return *this->threads[i]; }
+
+ // change the number of threads in the pool
+ // should be called from one thread, otherwise be careful to not interleave, also with this->stop()
+ // nThreads must be >= 0
+ void resize(int nThreads) {
+ if (!this->isStop && !this->isDone) {
+ int oldNThreads = static_cast(this->threads.size());
+ if (oldNThreads <= nThreads) { // if the number of threads is increased
+ this->threads.resize(nThreads);
+ this->flags.resize(nThreads);
+
+ for (int i = oldNThreads; i < nThreads; ++i) {
+ this->flags[i] = std::make_shared>(false);
+ this->set_thread(i);
+ }
+ }
+ else { // the number of threads is decreased
+ for (int i = oldNThreads - 1; i >= nThreads; --i) {
+ *this->flags[i] = true; // this thread will finish
+ this->threads[i]->detach();
+ }
+ {
+ // stop the detached threads that were waiting
+ std::unique_lock lock(this->mutex);
+ this->cv.notify_all();
+ }
+ this->threads.resize(nThreads); // safe to delete because the threads are detached
+ this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals
+ }
+ }
+ }
+
+ // empty the queue
+ void clear_queue() {
+ std::function * _f;
+ while (this->q.pop(_f))
+ delete _f; // empty the queue
+ }
+
+ // pops a functional wrapper to the original function
+ std::function pop() {
+ std::function * _f = nullptr;
+ this->q.pop(_f);
+ std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred
+ std::function f;
+ if (_f)
+ f = *_f;
+ return f;
+ }
+
+ // wait for all computing threads to finish and stop all threads
+ // may be called asynchronously to not pause the calling thread while waiting
+ // if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions
+ void stop(bool isWait = false) {
+ if (!isWait) {
+ if (this->isStop)
+ return;
+ this->isStop = true;
+ for (int i = 0, n = this->size(); i < n; ++i) {
+ *this->flags[i] = true; // command the threads to stop
+ }
+ this->clear_queue(); // empty the queue
+ }
+ else {
+ if (this->isDone || this->isStop)
+ return;
+ this->isDone = true; // give the waiting threads a command to finish
+ }
+ {
+ std::unique_lock lock(this->mutex);
+ this->cv.notify_all(); // stop all waiting threads
+ }
+ for (int i = 0; i < static_cast(this->threads.size()); ++i) { // wait for the computing threads to finish
+ if (this->threads[i]->joinable())
+ this->threads[i]->join();
+ }
+ // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads
+ // therefore delete them here
+ this->clear_queue();
+ this->threads.clear();
+ this->flags.clear();
+ }
+
+ template
+ auto push(F && f, Rest&&... rest) ->std::future {
+ auto pck = std::make_shared>(
+ std::bind(std::forward(f), std::placeholders::_1, std::forward(rest)...)
+ );
+ auto _f = new std::function([pck](int id) {
+ (*pck)(id);
+ });
+ ++this->nPending;
+ this->q.push(_f);
+ std::unique_lock lock(this->mutex);
+ this->cv.notify_one();
+ return pck->get_future();
+ }
+
+ // run the user's function that excepts argument int - id of the running thread. returned value is templatized
+ // operator returns std::future, where the user can get the result and rethrow the catched exceptins
+ template
+ auto push(F && f) ->std::future {
+ auto pck = std::make_shared>(std::forward(f));
+ auto _f = new std::function([pck](int id) {
+ (*pck)(id);
+ });
+ ++this->nPending;
+ this->q.push(_f);
+ std::unique_lock lock(this->mutex);
+ this->cv.notify_one();
+ return pck->get_future();
+ }
+
+
+ private:
+
+ // deleted
+ thread_pool(const thread_pool &);// = delete;
+ thread_pool(thread_pool &&);// = delete;
+ thread_pool & operator=(const thread_pool &);// = delete;
+ thread_pool & operator=(thread_pool &&);// = delete;
+
+ void set_thread(int i) {
+ std::shared_ptr> flag(this->flags[i]); // a copy of the shared ptr to the flag
+ auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() {
+ std::atomic & _flag = *flag;
+ std::function * _f;
+ bool isPop = this->q.pop(_f);
+ while (true) {
+ while (isPop) { // if there is anything in the queue
+ --this->nPending;
+ std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred
+ (*_f)(i);
+ if (_flag)
+ return; // the thread is wanted to stop, return even if the queue is not empty yet
+ else
+ isPop = this->q.pop(_f);
+ }
+ // the queue is empty here, wait for the next command
+ std::unique_lock lock(this->mutex);
+ ++this->nWaiting;
+ this->cv.wait(lock, [this, &_f, &isPop, &_flag]() { isPop = this->q.pop(_f); return isPop || this->isDone || _flag; });
+ --this->nWaiting;
+ if (!isPop)
+ return; // if the queue is empty and this->isDone == true or *flag then return
+ }
+ };
+ this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique()
+ }
+
+ void init() { this->nWaiting = 0; this->nPending = 0; this->isStop = false; this->isDone = false; }
+
+ std::vector> threads;
+ std::vector>> flags;
+ detail::Queue *> q;
+ std::atomic isDone;
+ std::atomic isStop;
+ std::atomic nWaiting; // how many threads are waiting
+ std::atomic nPending; // how many tasks are waiting
+
+ std::mutex mutex;
+ std::condition_variable cv;
+ };
+
+}
+
+#endif // __ctpl_stl_thread_pool_H__
\ No newline at end of file
diff --git a/src/main/protocol/memory/habboclient/windows/WindowsHabboClient.java b/src/main/protocol/memory/habboclient/windows/WindowsHabboClient.java
index f5ba48e..1c5ab16 100644
--- a/src/main/protocol/memory/habboclient/windows/WindowsHabboClient.java
+++ b/src/main/protocol/memory/habboclient/windows/WindowsHabboClient.java
@@ -1,5 +1,6 @@
package main.protocol.memory.habboclient.windows;
+import main.misc.Cacher;
import main.protocol.HConnection;
import main.protocol.HMessage;
import main.protocol.TrafficListener;
@@ -32,22 +33,61 @@ public class WindowsHabboClient extends HabboClient {
@Override
public List getRC4cached() {
- return new ArrayList<>();
+ List result = new ArrayList<>();
+ try {
+ List possibleResults = readPossibleBytes(true);
+
+ if (possibleResults == null)
+ return new ArrayList<>();
+
+ for (String s : possibleResults)
+ result.add(hexStringToByteArray(s));
+ } catch (IOException | URISyntaxException e) {
+ e.printStackTrace();
+ }
+ return result;
}
- private ArrayList readPossibleBytes() throws IOException, URISyntaxException {
- ProcessBuilder pb = new ProcessBuilder(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "\\G-WinMem.exe", hConnection.getClientHostAndPort().substring(0, hConnection.getClientHostAndPort().indexOf(':')) , Integer.toString(hConnection.getPort()));
+ private ArrayList readPossibleBytes(boolean useCache) throws IOException, URISyntaxException {
+ ProcessBuilder pb = null;
+ List cachedOffsets = (List) Cacher.get("RC4Offsets");
+ StringJoiner joiner = new StringJoiner(" ");
+
+ if (useCache) {
+ if (cachedOffsets == null) {
+ return null;
+ }
+
+ for (String s : cachedOffsets) {
+ joiner.add(s);
+ }
+ }
+
+ if (!useCache)
+ pb = new ProcessBuilder(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "\\G-WinMem.exe", hConnection.getClientHostAndPort().substring(0, hConnection.getClientHostAndPort().indexOf(':')) , Integer.toString(hConnection.getPort()));
+ else
+ pb = new ProcessBuilder(new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "\\G-WinMem.exe", hConnection.getClientHostAndPort().substring(0, hConnection.getClientHostAndPort().indexOf(':')) , Integer.toString(hConnection.getPort()), "-c" + joiner.toString());
+
+
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
ArrayList possibleData = new ArrayList<>();
+ cachedOffsets = new ArrayList<>();
+
+ int count = 0;
while((line = reader.readLine()) != null) {
if (line.length() > 1) {
- possibleData.add(line);
+ if (!useCache && (count++ % 2 == 0)) {
+ cachedOffsets.add(line);
+ }
+ else
+ possibleData.add(line);
}
}
+ Cacher.put("RC4Offsets", cachedOffsets);
p.destroy();
return possibleData;
}
@@ -56,7 +96,7 @@ public class WindowsHabboClient extends HabboClient {
public List getRC4possibilities() {
List result = new ArrayList<>();
try {
- ArrayList possibleData = readPossibleBytes();
+ ArrayList possibleData = readPossibleBytes(false);
for (String possibleHexStr : possibleData) {
result.add(hexStringToByteArray(possibleHexStr));