mirror of
https://github.com/HIllya51/LunaHook.git
synced 2025-01-11 20:39:34 +08:00
plugin
This commit is contained in:
parent
7ae1e01aa3
commit
ebe0048bb2
@ -46,6 +46,7 @@
|
||||
#define BtnRefresh L"Refresh"
|
||||
#define BtnToClipboard L"to clipboard"
|
||||
#define BtnInsertUserHook L"Insert UserHook"
|
||||
#define BtnAddPlugin L"Add Plugin"
|
||||
#define LblFlushDelay L"flushDelay"
|
||||
#define LblCodePage L"CodePage"
|
||||
#define MenuCopyHookCode L"CopyHookCode"
|
||||
|
@ -533,7 +533,7 @@ namespace Private {
|
||||
|
||||
// Convert to lower case
|
||||
std::string s = t;
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
stolower(s);
|
||||
t = s.c_str();
|
||||
|
||||
if (::strchr(t, '_')) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
add_executable(LunaHost WIN32 controls.cpp main.cpp processlistwindow.cpp LunaHost.cpp window.cpp luna.rc)
|
||||
add_executable(LunaHost WIN32 controls.cpp main.cpp processlistwindow.cpp LunaHost.cpp window.cpp luna.rc pluginmanager.cpp pluginexample.cpp)
|
||||
target_precompile_headers(LunaHost REUSE_FROM pch)
|
||||
set_target_properties(LunaHost PROPERTIES OUTPUT_NAME "LunaHost${bitappendix}")
|
||||
|
||||
|
@ -1,15 +1,14 @@
|
||||
|
||||
#include <CommCtrl.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <commdlg.h>
|
||||
#include<stdio.h>
|
||||
|
||||
#include"host.h"
|
||||
#include"hookcode.h"
|
||||
#include"textthread.h"
|
||||
#include"LunaHost.h"
|
||||
#include"processlistwindow.h"
|
||||
#include"Lang/Lang.h"
|
||||
|
||||
void LunaHost::toclipboard(std::wstring& sentence){
|
||||
|
||||
for (int loop = 0; loop < 10; loop++) {
|
||||
@ -35,13 +34,36 @@ void LunaHost::on_size(int w,int h){
|
||||
g_hListBox_listtext->setgeo(10, 110, w - 20, height/2);
|
||||
g_showtexts->setgeo(10, 120+height/2, w - 20, height/2);
|
||||
}
|
||||
std::optional<std::wstring>SelectFile(HWND hwnd){
|
||||
OPENFILENAME ofn;
|
||||
wchar_t szFileName[MAX_PATH] = { 0 };
|
||||
|
||||
ZeroMemory(&ofn, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.hwndOwner = hwnd;
|
||||
ofn.lpstrFilter = L"Plugin Files (.dll)\0*.dll\0";
|
||||
ofn.lpstrFile = szFileName;
|
||||
ofn.nMaxFile = sizeof(szFileName);
|
||||
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
|
||||
|
||||
if (GetOpenFileName(&ofn))
|
||||
{
|
||||
return szFileName;
|
||||
}
|
||||
else return {};
|
||||
}
|
||||
LunaHost::LunaHost(){
|
||||
settext(WndLunaHostGui);
|
||||
g_selectprocessbutton =new button(this,BtnSelectProcess,780, 10, 200, 40) ;
|
||||
g_selectprocessbutton =new button(this,BtnSelectProcess,830, 10, 200, 40) ;
|
||||
|
||||
g_hEdit_userhook = new textedit(this,L"",10, 60, 600, 40,ES_AUTOHSCROLL);
|
||||
|
||||
btnaddplugin=new button(this,BtnAddPlugin,830,60,200,40);
|
||||
btnaddplugin->onclick=[&](){
|
||||
auto f=SelectFile(winId);
|
||||
if(f.has_value()){
|
||||
plugins->addplugin(f.value());
|
||||
}
|
||||
};
|
||||
g_hButton_insert = new button(this,BtnInsertUserHook,610, 60, 200, 40) ;
|
||||
|
||||
g_selectprocessbutton->onclick=[&](){
|
||||
@ -107,67 +129,106 @@ LunaHost::LunaHost(){
|
||||
};
|
||||
g_hListBox_listtext->oncontextmenucallback=[&](WPARAM wparam){
|
||||
|
||||
int handle = g_hListBox_listtext->getdata(g_hListBox_listtext->currentidx());
|
||||
auto handle = g_hListBox_listtext->getdata(g_hListBox_listtext->currentidx());
|
||||
auto tt=Host::GetThread(handle);
|
||||
if(tt==0)return;
|
||||
switch (LOWORD(wparam)) {
|
||||
|
||||
case IDM_COPY_HOOKCODE:
|
||||
toclipboard(std::wstring(savehooks[handle]->hp.hookcode));
|
||||
toclipboard(std::wstring(tt->hp.hookcode));
|
||||
break;
|
||||
case IDM_DETACH_PROCESS:
|
||||
Host::DetachProcess(savehooks[handle]->tp.processId);
|
||||
Host::DetachProcess(tt->tp.processId);
|
||||
break;
|
||||
case IDM_REMOVE_HOOK:
|
||||
Host::RemoveHook(savehooks[handle]->tp.processId,savehooks[handle]->tp.addr);
|
||||
Host::RemoveHook(tt->tp.processId,tt->tp.addr);
|
||||
break;
|
||||
}
|
||||
};
|
||||
g_showtexts = new textedit(this,L"",10, 330, 200, 200,ES_READONLY|ES_MULTILINE |ES_AUTOVSCROLL| WS_VSCROLL);
|
||||
|
||||
|
||||
plugins=new pluginmanager;
|
||||
|
||||
Host::Start(
|
||||
[&](DWORD pid) {attachedprocess.push_back(pid);},
|
||||
[&](DWORD pid) {
|
||||
attachedprocess.erase(std::remove(attachedprocess.begin(), attachedprocess.end(), pid), attachedprocess.end());
|
||||
},
|
||||
[&](TextThread& thread) {
|
||||
wchar_t buff[65535];
|
||||
swprintf_s(buff,L"[%I64X:%I32X:%I64X:%I64X:%I64X:%s:%s]",
|
||||
thread.handle,
|
||||
thread.tp.processId,
|
||||
thread.tp.addr,
|
||||
thread.tp.ctx,
|
||||
thread.tp.ctx2,
|
||||
thread.name.c_str(),
|
||||
thread.hp.hookcode
|
||||
);
|
||||
savetext.insert({thread.handle,{}});
|
||||
int index=g_hListBox_listtext->additem(buff);
|
||||
g_hListBox_listtext->setdata(index,thread.handle);
|
||||
savehooks.insert(std::make_pair(thread.handle,&thread));
|
||||
},
|
||||
[&](TextThread& thread) {
|
||||
int count = g_hListBox_listtext->count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint64_t handle = g_hListBox_listtext->getdata(i);
|
||||
|
||||
if (handle== thread.handle) {
|
||||
g_hListBox_listtext->deleteitem(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[&](TextThread& thread, std::wstring& output)
|
||||
{
|
||||
std::lock_guard _(settextmutex);
|
||||
std::wstring lfoutput=output;
|
||||
strReplace(lfoutput,L"\n",L"\r\n");
|
||||
savetext.at(thread.handle).push_back(lfoutput);
|
||||
if(currentselect==thread.handle){
|
||||
g_showtexts->scrolltoend();
|
||||
g_showtexts->appendtext(lfoutput);
|
||||
if(check_toclipboard)
|
||||
toclipboard(output);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::bind(&LunaHost::on_thread_create,this,std::placeholders::_1),
|
||||
std::bind(&LunaHost::on_thread_delete,this,std::placeholders::_1),
|
||||
std::bind(&LunaHost::on_text_recv,this,std::placeholders::_1,std::placeholders::_2)
|
||||
);
|
||||
}
|
||||
|
||||
std::array<InfoForExtension, 20> LunaHost::GetSentenceInfo(TextThread& thread)
|
||||
{
|
||||
void (*AddText)(int64_t, const wchar_t*) = [](int64_t number, const wchar_t* text)
|
||||
{
|
||||
if (TextThread* thread = Host::GetThread(number)) thread->Push(text);
|
||||
};
|
||||
void (*AddSentence)(int64_t, const wchar_t*) = [](int64_t number, const wchar_t* sentence)
|
||||
{
|
||||
if (TextThread* thread = Host::GetThread(number)) thread->AddSentence(sentence);;
|
||||
};
|
||||
static DWORD SelectedProcessId;
|
||||
auto currthread=Host::GetThread(currentselect);
|
||||
SelectedProcessId=(currthread!=0)?currthread->tp.processId:0;
|
||||
DWORD (*GetSelectedProcessId)() = [] { return SelectedProcessId; };
|
||||
|
||||
return
|
||||
{ {
|
||||
{ "HostHWND",(int64_t)winId },
|
||||
{ "toclipboard", check_toclipboard },
|
||||
{ "current select", &thread == currthread },
|
||||
{ "text number", thread.handle },
|
||||
{ "process id", thread.tp.processId },
|
||||
{ "hook address", (int64_t)thread.tp.addr },
|
||||
{ "text handle", thread.handle },
|
||||
{ "text name", (int64_t)thread.name.c_str() },
|
||||
{ "add sentence", (int64_t)AddSentence },
|
||||
{ "add text", (int64_t)AddText },
|
||||
{ "get selected process id", (int64_t)GetSelectedProcessId },
|
||||
{ "void (*AddSentence)(int64_t number, const wchar_t* sentence)", (int64_t)AddSentence },
|
||||
{ "void (*AddText)(int64_t number, const wchar_t* text)", (int64_t)AddText },
|
||||
{ "DWORD (*GetSelectedProcessId)()", (int64_t)GetSelectedProcessId },
|
||||
{ nullptr, 0 } // nullptr marks end of info array
|
||||
} };
|
||||
}
|
||||
bool LunaHost::on_text_recv(TextThread& thread, std::wstring& output){
|
||||
std::lock_guard _(settextmutex);
|
||||
std::wstring lfoutput=output;
|
||||
if(!plugins->dispatch(GetSentenceInfo(thread).data(),output))return false;
|
||||
strReplace(lfoutput,L"\n",L"\r\n");
|
||||
savetext.at(thread.handle).push_back(lfoutput);
|
||||
if(currentselect==thread.handle){
|
||||
g_showtexts->scrolltoend();
|
||||
g_showtexts->appendtext(lfoutput);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void LunaHost::on_thread_create(TextThread& thread){
|
||||
wchar_t buff[65535];
|
||||
swprintf_s(buff,L"[%I64X:%I32X:%I64X:%I64X:%I64X:%s:%s]",
|
||||
thread.handle,
|
||||
thread.tp.processId,
|
||||
thread.tp.addr,
|
||||
thread.tp.ctx,
|
||||
thread.tp.ctx2,
|
||||
thread.name.c_str(),
|
||||
thread.hp.hookcode
|
||||
);
|
||||
savetext.insert({thread.handle,{}});
|
||||
int index=g_hListBox_listtext->additem(buff);
|
||||
g_hListBox_listtext->setdata(index,thread.handle);
|
||||
}
|
||||
void LunaHost::on_thread_delete(TextThread& thread){
|
||||
int count = g_hListBox_listtext->count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint64_t handle = g_hListBox_listtext->getdata(i);
|
||||
|
||||
if (handle== thread.handle) {
|
||||
g_hListBox_listtext->deleteitem(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,16 +2,18 @@
|
||||
#include"controls.h"
|
||||
#include"processlistwindow.h"
|
||||
#include"textthread.h"
|
||||
#include"pluginmanager.h"
|
||||
#include"plugin.h"
|
||||
class LunaHost:public mainwindow{
|
||||
|
||||
int64_t currentselect=0;
|
||||
std::map<int64_t,std::vector<std::wstring>>savetext;
|
||||
std::vector<int>attachedprocess;
|
||||
bool check_toclipboard=false;
|
||||
std::map<int,TextThread*>savehooks;
|
||||
std::mutex settextmutex;
|
||||
textedit* g_hEdit_userhook;
|
||||
button* g_hButton_insert;
|
||||
button* btnaddplugin;
|
||||
listbox* g_hListBox_listtext;
|
||||
textedit* g_showtexts;
|
||||
button* g_selectprocessbutton;
|
||||
@ -20,6 +22,11 @@ class LunaHost:public mainwindow{
|
||||
checkbox* g_check_clipboard;
|
||||
void toclipboard(std::wstring& sentence);
|
||||
processlistwindow *_processlistwindow=0;
|
||||
pluginmanager* plugins;
|
||||
bool on_text_recv(TextThread& thread, std::wstring& sentence);
|
||||
void on_thread_create(TextThread& thread);
|
||||
void on_thread_delete(TextThread& thread);
|
||||
std::array<InfoForExtension, 20> GetSentenceInfo(TextThread& thread);
|
||||
public:
|
||||
void on_size(int w,int h);
|
||||
void on_close();
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#include"LunaHost.h"
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||
//int main()
|
||||
|
22
LunaHost/GUI/plugin.h
Normal file
22
LunaHost/GUI/plugin.h
Normal file
@ -0,0 +1,22 @@
|
||||
#include<Windows.h>
|
||||
#ifndef LUNA_PLUGIN_DEF_H
|
||||
#define LUNA_PLUGIN_DEF_H
|
||||
struct InfoForExtension
|
||||
{
|
||||
const char* name;
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
struct SentenceInfo
|
||||
{
|
||||
const InfoForExtension* infoArray;
|
||||
int64_t operator[](std::string_view propertyName)
|
||||
{
|
||||
for (auto info = infoArray; info->name; ++info) // nullptr name marks end of info array
|
||||
if (propertyName == info->name) return info->value;
|
||||
return *(int*)0xDEAD = 0; // gives better error message than alternatives
|
||||
}
|
||||
};
|
||||
typedef wchar_t* (*OnNewSentence_t)(wchar_t*, const InfoForExtension*);
|
||||
|
||||
#endif
|
37
LunaHost/GUI/pluginexample.cpp
Normal file
37
LunaHost/GUI/pluginexample.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include"plugin.h"
|
||||
|
||||
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo);
|
||||
|
||||
extern "C" __declspec(dllexport) wchar_t* OnNewSentence(wchar_t* sentence, const InfoForExtension* sentenceInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::wstring sentenceCopy(sentence);
|
||||
int oldSize = sentenceCopy.size();
|
||||
if (ProcessSentence(sentenceCopy, SentenceInfo{ sentenceInfo }))
|
||||
{
|
||||
if (sentenceCopy.size() > oldSize) sentence = (wchar_t*)HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sentence, (sentenceCopy.size() + 1) * sizeof(wchar_t));
|
||||
wcscpy_s(sentence, sentenceCopy.size() + 1, sentenceCopy.c_str());
|
||||
}
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
*sentence = L'\0';
|
||||
}
|
||||
return sentence;
|
||||
}
|
||||
|
||||
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||
{
|
||||
if (sentenceInfo["current select"] && sentenceInfo["process id"] != 0 &&sentenceInfo["toclipboard"])
|
||||
{
|
||||
if (!OpenClipboard((HWND)sentenceInfo["HostHWND"])) return false;
|
||||
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (sentence.size() + 2) * sizeof(wchar_t));
|
||||
memcpy(GlobalLock(hMem), sentence.c_str(), (sentence.size() + 2) * sizeof(wchar_t));
|
||||
EmptyClipboard();
|
||||
SetClipboardData(CF_UNICODETEXT, hMem);
|
||||
GlobalUnlock(hMem);
|
||||
CloseClipboard();
|
||||
}
|
||||
return false;
|
||||
}
|
99
LunaHost/GUI/pluginmanager.cpp
Normal file
99
LunaHost/GUI/pluginmanager.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
#include"pluginmanager.h"
|
||||
#include<filesystem>
|
||||
#include"plugin.h"
|
||||
#include<fstream>
|
||||
std::vector<char> readfile(const wchar_t* fname) {
|
||||
FILE* f;
|
||||
_wfopen_s(&f, fname, L"rb");
|
||||
if (f == 0)return {};
|
||||
fseek(f, 0, SEEK_END);
|
||||
auto len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
auto buff = std::vector<char>(len);
|
||||
fread(buff.data(), 1, len, f);
|
||||
fclose(f);
|
||||
return buff;
|
||||
}
|
||||
std::vector<std::wstring>pluginmanager::readpluginfile(){
|
||||
if(!std::filesystem::exists(pluginfilename))
|
||||
return{};
|
||||
auto u16pl=StringToWideString(readfile(pluginfilename.c_str()).data());
|
||||
auto pls=strSplit(u16pl,L"\n");
|
||||
return pls;
|
||||
}
|
||||
void pluginmanager::writepluginfile(const std::wstring& plugf){
|
||||
auto u8s=WideStringToString(plugf);
|
||||
FILE* f;
|
||||
_wfopen_s(&f, pluginfilename.c_str(), L"a");
|
||||
fprintf(f,"\n%s",u8s.c_str());
|
||||
fclose(f);
|
||||
}
|
||||
pluginmanager::pluginmanager(){
|
||||
try {
|
||||
std::scoped_lock lock(OnNewSentenceSLock);
|
||||
pluginfilename=std::filesystem::current_path()/(x64?"plugin64.txt":"plugin32.txt");
|
||||
OnNewSentenceS.push_back({L"Internal ClipBoard",GetProcAddress(GetModuleHandle(0),"OnNewSentence")});//内部链接的剪贴板插件
|
||||
|
||||
for (auto &pl:readpluginfile()) {
|
||||
|
||||
auto ret=checkisvalidplugin(pl);
|
||||
if(ret.has_value()){
|
||||
auto v=ret.value();
|
||||
if(!checkisdump(v.second)){
|
||||
OnNewSentenceS.push_back(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
std::cerr << "Error: " << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool pluginmanager::dispatch(const InfoForExtension* sentenceInfo, std::wstring& sentence){
|
||||
wchar_t* sentenceBuffer = (wchar_t*)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, (sentence.size() + 1) * sizeof(wchar_t));
|
||||
wcscpy_s(sentenceBuffer, sentence.size() + 1, sentence.c_str());
|
||||
concurrency::reader_writer_lock::scoped_lock_read readLock(OnNewSentenceSLock);
|
||||
for (const auto& extension : OnNewSentenceS){
|
||||
if (!*(sentenceBuffer = ((OnNewSentence_t)extension.second)(sentenceBuffer, sentenceInfo))) break;
|
||||
}
|
||||
sentence = sentenceBuffer;
|
||||
HeapFree(GetProcessHeap(), 0, sentenceBuffer);
|
||||
return !sentence.empty();
|
||||
}
|
||||
std::optional<std::pair<std::wstring,LPVOID>> pluginmanager::checkisvalidplugin(const std::wstring& pl){
|
||||
auto path=std::filesystem::path(pl);
|
||||
if (!std::filesystem::exists(path))return{};
|
||||
if (!std::filesystem::is_regular_file(path))return{};
|
||||
auto appendix=path.extension().wstring();
|
||||
stolower(appendix);
|
||||
if(appendix!=std::wstring(L".dll"))return {};
|
||||
auto dll=LoadLibraryW(pl.c_str());
|
||||
if(!dll)return {};
|
||||
auto OnNewSentence=GetProcAddress(dll,"OnNewSentence");
|
||||
if(!OnNewSentence){
|
||||
FreeLibrary(dll);
|
||||
return {};
|
||||
}
|
||||
return std::make_pair(path.stem(), OnNewSentence);
|
||||
}
|
||||
bool pluginmanager::checkisdump(LPVOID ptr){
|
||||
for(auto& p:OnNewSentenceS){
|
||||
if(p.second==ptr)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool pluginmanager::addplugin(const std::wstring& p){
|
||||
auto plugin=checkisvalidplugin(p);
|
||||
if(plugin.has_value()){
|
||||
auto v=plugin.value();
|
||||
std::scoped_lock lock(OnNewSentenceSLock);
|
||||
if(!checkisdump(v.second)){
|
||||
OnNewSentenceS.push_back(v);
|
||||
writepluginfile(p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
16
LunaHost/GUI/pluginmanager.h
Normal file
16
LunaHost/GUI/pluginmanager.h
Normal file
@ -0,0 +1,16 @@
|
||||
#include"plugin.h"
|
||||
#include"textthread.h"
|
||||
class pluginmanager{
|
||||
std::vector<std::pair<std::wstring,LPVOID>>OnNewSentenceS;
|
||||
std::optional<std::pair<std::wstring,LPVOID>> checkisvalidplugin(const std::wstring&);
|
||||
std::vector<std::wstring>readpluginfile();
|
||||
void writepluginfile(const std::wstring&);
|
||||
std::wstring pluginfilename;
|
||||
concurrency::reader_writer_lock OnNewSentenceSLock;
|
||||
bool checkisdump(LPVOID);
|
||||
public:
|
||||
pluginmanager();
|
||||
bool dispatch(const InfoForExtension*, std::wstring& sentence);
|
||||
bool addplugin(const std::wstring&);
|
||||
};
|
||||
|
@ -30,8 +30,7 @@ std::unordered_map<std::wstring,std::vector<int>> getprocesslist()
|
||||
QueryFullProcessImageNameW(handle, 0, buff, &sz);
|
||||
|
||||
auto buffs=std::wstring(buff);
|
||||
auto str=std::wstring(buff);
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](wchar_t ch){ return std::tolower(ch, std::locale());});
|
||||
auto str=stolower(buffs);
|
||||
if(str.find(L"\\windows\\")!=str.npos || str.find(L"\\microsoft")!=str.npos|| str.find(L"\\windowsapps")!=str.npos)continue;
|
||||
|
||||
|
||||
|
@ -3,6 +3,11 @@
|
||||
|
||||
enum { VNR_TEXT_CAPACITY = 1500 }; // estimated max number of bytes allowed in VNR, slightly larger than VNR's text limit (1000)
|
||||
|
||||
template<class StringT>
|
||||
StringT& stolower(StringT& s){
|
||||
std::transform(s.begin(), s.end(), s.begin(), tolower);
|
||||
return s;
|
||||
}
|
||||
|
||||
LPCSTR reverse_search_begin(const char *s, int maxsize = VNR_TEXT_CAPACITY);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user