This commit is contained in:
恍兮惚兮 2024-02-08 16:18:33 +08:00
parent 7ae1e01aa3
commit ebe0048bb2
12 changed files with 302 additions and 56 deletions

View File

@ -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"

View File

@ -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, '_')) {

View File

@ -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}")

View File

@ -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;
}
}
}

View File

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

View File

@ -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
View 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

View 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;
}

View 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;
}
}

View 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&);
};

View File

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

View File

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