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));