#include #include #include #include #include #include"host.h" #include"hookcode.h" #include"textthread.h" #include"LunaHost.h" #include"Lang/Lang.h" auto gmf=[&](DWORD processId)->std::optional{ //见鬼了,GetModuleFileName找不到标识符 std::vector buffer(MAX_PATH); if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId)) if (GetModuleFileNameExW(process, 0, buffer.data(), MAX_PATH)) return buffer.data(); return {}; }; void LunaHost::toclipboard(std::wstring& sentence){ for (int loop = 0; loop < 10; loop++) { if (OpenClipboard(winId)) { 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(); break; } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } void LunaHost::on_close(){ savesettings(); delete configs; for(auto pid:attachedprocess){ Host::DetachProcess(pid); } if(attachedprocess.size()) std::this_thread::sleep_for(std::chrono::milliseconds(100)); } void LunaHost::savesettings() { configs->set("ToClipboard",check_toclipboard); configs->set("AutoAttach",autoattach); configs->set("AutoAttach_SavedOnly",autoattach_savedonly); configs->set("flushDelay",TextThread::flushDelay); configs->set("filterRepetition",TextThread::filterRepetition); configs->set("maxBufferSize",TextThread::maxBufferSize); configs->set("maxHistorySize",TextThread::maxHistorySize); configs->set("defaultCodepage",Host::defaultCodepage); configs->set("autoattachexes",autoattachexes); configs->set("savedhookcontext",savedhookcontext); } void LunaHost::loadsettings(){ check_toclipboard=configs->get("ToClipboard",false); autoattach=configs->get("AutoAttach",false); autoattach_savedonly=configs->get("AutoAttach_SavedOnly",true); TextThread::flushDelay=configs->get("flushDelay",TextThread::flushDelay); TextThread::filterRepetition=configs->get("filterRepetition",TextThread::filterRepetition); TextThread::maxBufferSize=configs->get("maxBufferSize",TextThread::maxBufferSize); TextThread::maxHistorySize=configs->get("maxHistorySize",TextThread::maxHistorySize); Host::defaultCodepage=configs->get("defaultCodepage",Host::defaultCodepage); autoattachexes=configs->get("autoattachexes",std::set{}); savedhookcontext=configs->get("savedhookcontext",decltype(savedhookcontext){}); } std::unordered_map> getprocesslist(); void LunaHost::doautoattach() { if(autoattach==false&&autoattach_savedonly==false)return; if(autoattachexes.empty())return; for(auto [pexe,pids ]:getprocesslist()) { auto&& u8procname=WideStringToString(pexe); if(autoattachexes.find(u8procname)==autoattachexes.end())continue; if(autoattach_savedonly && savedhookcontext.find(u8procname)==savedhookcontext.end())continue; for(auto pid:pids){ if(userdetachedpids.find(pid)!=userdetachedpids.end())continue; if(attachedprocess.find(pid)==attachedprocess.end()) Host::InjectProcess(pid); } break; } } void LunaHost::on_proc_connect(DWORD pid) { attachedprocess.insert(pid); if(auto pexe=GetModuleFilename(pid)) { autoattachexes.insert(WideStringToString(pexe.value())); } } LunaHost::LunaHost(){ configs=new confighelper; loadsettings(); std::thread([&]{ while(1){ doautoattach(); std::this_thread::sleep_for(std::chrono::seconds(2)); } }).detach(); setfont(25); settext(WndLunaHostGui); btnshowsettionwindow=new button(this, BtnShowSettingWindow); g_selectprocessbutton =new button(this,BtnSelectProcess) ; // btnsavehook=new button(this,BtnSaveHook,10,10,10,10); // btnsavehook->onclick=std::bind(&LunaHost::btnsavehookscallback,this); btndetachall=new button(this,BtnDetach); btndetachall->onclick=[&](){ for(auto pid:attachedprocess){ Host::DetachProcess(pid); userdetachedpids.insert(pid); } }; g_hEdit_userhook = new lineedit(this); btnplugin=new button(this,BtnPlugin); plugins=new Pluginmanager(this); btnplugin->onclick=[&](){ if(pluginwindow==0) pluginwindow=new Pluginwindow(this,plugins); pluginwindow->show(); }; g_hButton_insert = new button(this,BtnInsertUserHook) ; btnshowsettionwindow->onclick=[&](){ if(settingwindow==0) settingwindow=new Settingwindow(this); settingwindow->show(); }; g_selectprocessbutton->onclick=[&](){ if(_processlistwindow==0) _processlistwindow=new processlistwindow(this); _processlistwindow->show(); }; g_hButton_insert->onclick=[&](){ auto hp = HookCode::Parse(std::move(g_hEdit_userhook->text())); if(hp){ for(auto _:attachedprocess){ Host::InsertHook(_,hp.value()); } } else{ g_showtexts->appendtext(NotifyInvalidHookCode); } }; g_hListBox_listtext = new listbox(this); g_hListBox_listtext->oncurrentchange=[&](int idx){ uint64_t handle = g_hListBox_listtext->getdata(idx); std::wstring get; for(auto& _:savetext.at(handle)){ get+=_; get+=L"\r\n"; } currentselect=handle; g_showtexts->settext(get); g_showtexts->scrolltoend(); }; #define IDM_REMOVE_HOOK 1001 #define IDM_DETACH_PROCESS 1002 #define IDM_COPY_HOOKCODE 1003 #define IDM_REMEMBER_SELECTION 1004 #define IDM_FORGET_SELECTION 1005 g_hListBox_listtext->oncontextmenu=[](){ HMENU hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING, IDM_COPY_HOOKCODE, MenuCopyHookCode); AppendMenu(hMenu, MF_STRING, IDM_REMOVE_HOOK, MenuRemoveHook); AppendMenu(hMenu, MF_STRING, IDM_DETACH_PROCESS, MenuDetachProcess); AppendMenu(hMenu, MF_STRING, IDM_REMEMBER_SELECTION, MenuRemeberSelect); AppendMenu(hMenu, MF_STRING, IDM_FORGET_SELECTION, MenuForgetSelect); return hMenu; }; g_hListBox_listtext->oncontextmenucallback=[&](WPARAM wparam){ 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(tt->hp.hookcode)); break; case IDM_DETACH_PROCESS: Host::DetachProcess(tt->tp.processId); userdetachedpids.insert(tt->tp.processId); break; case IDM_REMOVE_HOOK: Host::RemoveHook(tt->tp.processId,tt->tp.addr); break; case IDM_FORGET_SELECTION: case IDM_REMEMBER_SELECTION: { if(auto pexe=gmf(tt->tp.processId)) { auto pexev=WideStringToString(pexe.value()); if(LOWORD(wparam)==IDM_REMEMBER_SELECTION) { savedhookcontext[pexev]={ {"hookcode",WideStringToString(tt->hp.hookcode)}, {"ctx1",tt->tp.ctx}, {"ctx2",tt->tp.ctx2}, }; } else if(LOWORD(wparam)==IDM_FORGET_SELECTION) { savedhookcontext.erase(pexev); } } } break; } }; g_showtexts = new multilineedit(this); g_showtexts->setreadonly(true); btnsearchhooks=new button(this,BtnSearchHook); btnsearchhooks->onclick=[&](){ if(hooksearchwindow==0) hooksearchwindow=new Hooksearchwindow(this); hooksearchwindow->show(); }; Host::Start( std::bind(&LunaHost::on_proc_connect,this,std::placeholders::_1), [&](DWORD pid) { attachedprocess.erase(pid); }, 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) ); mainlayout=new gridlayout(); mainlayout->addcontrol(g_selectprocessbutton,0,0); mainlayout->addcontrol(btndetachall,0,1); mainlayout->addcontrol(btnshowsettionwindow,0,2); mainlayout->addcontrol(btnplugin,0,3); mainlayout->addcontrol(g_hEdit_userhook,1,0,1,2); mainlayout->addcontrol(g_hButton_insert,1,2); mainlayout->addcontrol(btnsearchhooks,1,3); mainlayout->addcontrol(g_hListBox_listtext,2,0,1,4); mainlayout->addcontrol(g_showtexts,3,0,1,4); mainlayout->setfixedheigth(0,30); mainlayout->setfixedheigth(1,30); setlayout(mainlayout); setcentral(1200,800); } void LunaHost::on_text_recv_checkissaved(TextThread& thread) { if(onceautoselectthread.find(thread.handle)!=onceautoselectthread.end())return; onceautoselectthread.insert(thread.handle); if(auto exe=GetModuleFilename(thread.tp.processId)) { auto exea=WideStringToString(exe.value()); if(savedhookcontext.find(exea)==savedhookcontext.end())return; std::string hc= savedhookcontext[exea]["hookcode"]; uint64_t ctx1=savedhookcontext[exea]["ctx1"]; uint64_t ctx2=savedhookcontext[exea]["ctx2"]; if(((ctx1&0xffff)==(thread.tp.ctx&0xffff) )&& (ctx2==thread.tp.ctx2) && (hc==WideStringToString(thread.hp.hookcode))) { for(int i=0;icount();i++) { auto handle=g_hListBox_listtext->getdata(i); if(handle==thread.handle) { g_hListBox_listtext->setcurrent(i); break; } } } } } bool LunaHost::on_text_recv(TextThread& thread, std::wstring& output){ std::lock_guard _(settextmutex); on_text_recv_checkissaved(thread); if(!plugins->dispatch(thread,output))return false; strReplace(output,L"\n",L"\r\n"); savetext.at(thread.handle).push_back(output); if(currentselect==thread.handle){ g_showtexts->scrolltoend(); g_showtexts->appendtext(output); } 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; } } } Settingwindow::Settingwindow(LunaHost* host):mainwindow(host){ int height=30;int curry=10;int space=10; int labelwidth=300;int spinwidth=200; g_timeout = new spinbox(this,TextThread::flushDelay) ; g_codepage = new spinbox(this,Host::defaultCodepage) ; spinmaxbuffsize = new spinbox(this,TextThread::maxBufferSize); ;curry+=height+space; spinmaxbuffsize->onvaluechange=[=](int v){ TextThread::maxBufferSize=v; }; spinmaxhistsize = new spinbox(this,TextThread::maxHistorySize) ; ;curry+=height+space; spinmaxhistsize->onvaluechange=[=](int v){ TextThread::maxHistorySize=v; }; ckbfilterrepeat=new checkbox(this,LblFilterRepeat); ckbfilterrepeat->onclick=[=](){ TextThread::filterRepetition=ckbfilterrepeat->ischecked(); }; ckbfilterrepeat->setcheck(TextThread::filterRepetition); g_check_clipboard =new checkbox(this,BtnToClipboard); g_check_clipboard->onclick=[=](){ host->check_toclipboard=g_check_clipboard->ischecked(); }; g_check_clipboard->setcheck(host->check_toclipboard); autoattach =new checkbox(this,LblAutoAttach); autoattach->onclick=[=](){ host->autoattach=autoattach->ischecked(); }; autoattach->setcheck(host->autoattach); autoattach_so =new checkbox(this,LblAutoAttach_savedonly); autoattach_so->onclick=[=](){ host->autoattach_savedonly=autoattach_so->ischecked(); }; autoattach_so->setcheck(host->autoattach_savedonly); readonlycheck=new checkbox(this,BtnReadOnly); readonlycheck->onclick=[=](){ host->g_showtexts->setreadonly(readonlycheck->ischecked()); }; readonlycheck->setcheck(true); g_timeout->onvaluechange=[=](int v){ TextThread::flushDelay=v; }; g_codepage->onvaluechange=[=](int v){ if(IsValidCodePage(v)){ Host::defaultCodepage= v; } }; g_codepage->setminmax(0,CP_UTF8); mainlayout=new gridlayout(); mainlayout->addcontrol(new label(this,LblFlushDelay),0,0); mainlayout->addcontrol(g_timeout,0,1); mainlayout->addcontrol(new label(this,LblCodePage),1,0); mainlayout->addcontrol(g_codepage,1,1); mainlayout->addcontrol(new label(this,LblMaxBuff),2,0); mainlayout->addcontrol(spinmaxbuffsize,2,1); mainlayout->addcontrol(new label(this,LblMaxHist),3,0); mainlayout->addcontrol(spinmaxhistsize,3,1); mainlayout->addcontrol(ckbfilterrepeat,4,0,1,2); mainlayout->addcontrol(g_check_clipboard,5,0,1,2); mainlayout->addcontrol(autoattach,6,0,1,2); mainlayout->addcontrol(autoattach_so,7,0,1,2); mainlayout->addcontrol(readonlycheck,8,0,1,2); setlayout(mainlayout); setcentral(600,500); settext(WndSetting); } void Pluginwindow::on_size(int w,int h){ listplugins->setgeo(10,80,w-20,h-90); } Pluginwindow::Pluginwindow(mainwindow*p,Pluginmanager* pl):mainwindow(p){ pluginmanager=pl; (new label(this,LblPluginNotify))->setgeo(10,10,500,30); (new label(this,LblPluginRemove))->setgeo( 10,40,500,30); static auto listadd=[&](const std::wstring& s){ auto idx=listplugins->additem(std::filesystem::path(s).stem()); auto _s=new wchar_t[s.size()+1];wcscpy(_s,s.c_str()); listplugins->setdata(idx,(LONG_PTR)_s); }; listplugins = new listbox(this); #define IDM_ADD_PLUGIN 1004 #define IDM_REMOVE_PLUGIN 1006 #define IDM_RANK_UP 1007 #define IDM_RANK_DOWN 1008 listplugins->oncontextmenu=[](){ HMENU hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING, IDM_ADD_PLUGIN, MenuAddPlugin); AppendMenu(hMenu, MF_STRING, IDM_REMOVE_PLUGIN, MenuRemovePlugin); AppendMenu(hMenu, MF_STRING, IDM_RANK_UP, MenuPluginRankUp); AppendMenu(hMenu, MF_STRING, IDM_RANK_DOWN, MenuPluginRankDown); return hMenu; }; listplugins->oncontextmenucallback=[&](WPARAM wparam){ switch (LOWORD(wparam)) { case IDM_RANK_DOWN: case IDM_RANK_UP: { auto idx=listplugins->currentidx(); if(idx==-1)break; auto idx2=idx+( (LOWORD(wparam)==IDM_RANK_UP)?-1:1 ); auto a=min(idx,idx2),b=max(idx,idx2); if(a<0||b>=listplugins->count())break; pluginmanager->swaprank(a,b); auto pa=((LPCWSTR)listplugins->getdata(a)); auto pb=((LPCWSTR)listplugins->getdata(b)); listplugins->deleteitem(a); listplugins->insertitem(b,std::filesystem::path(pa).stem()); listplugins->setdata(b,(LONG_PTR)pa); break; } case IDM_ADD_PLUGIN: { auto f=pluginmanager->selectpluginfile(); if(f.has_value()){ if(pluginmanager->addplugin(f.value())){ listadd(f.value()); } else{ MessageBoxW(winId,InvalidPlugin,MsgError,0); } } break; } case IDM_REMOVE_PLUGIN: { auto idx=listplugins->currentidx(); if(idx==-1)break; pluginmanager->remove((LPCWSTR)listplugins->getdata(idx)); listplugins->deleteitem(idx); break; } } }; for(int i=0;iPluginRank.size();i++){ listadd(pluginmanager->PluginRank[i]); } settext(WndPlugins); setcentral(500,400); } void HooksearchText::call(std::setpids){ edittext->settext(L""); checkok->onclick=[&,pids](){ close(); auto cp=codepage->getcurr(); if(!IsValidCodePage(cp)) return; SearchParam sp = {}; sp.codepage=cp; wcsncpy_s(sp.text, edittext->text().c_str(), PATTERN_SIZE - 1); wprintf(sp.text); for(auto pid:pids) Host::FindHooks(pid, sp); }; show(); } HooksearchText::HooksearchText(mainwindow* p):mainwindow(p){ codepage=new spinbox(this,Host::defaultCodepage); codepage->setminmax(0,CP_UTF8); edittext=new lineedit(this); checkok=new button(this,BtnOk); layout=new gridlayout(); layout->addcontrol(new label(this,HS_TEXT),0,0); layout->addcontrol(new label(this,HS_CODEPAGE),1,0); layout->addcontrol(edittext,0,1); layout->addcontrol(codepage,1,1); layout->addcontrol(checkok,2,1); setlayout(layout); setcentral(500,200); } std::wstring tohex(BYTE* bs,int len){ std::wstring buffer; for (int i = 0; i < len; i += 1){ buffer.append(FormatString(L"%02hX ", bs[i])); } return buffer; } std::wstring addr2hex(uintptr_t addr){ return FormatString(L"%p",addr); } void realcallsearchhooks(std::setpids,std::wstring filter,SearchParam sp){ auto hooks = std::make_shared>(); try { for(auto processId:pids) Host::FindHooks(processId, sp, [hooks, filter](HookParam hp, std::wstring text) { std::wsmatch matches; if (std::regex_search(text, matches, std::wregex(filter))) { hooks->emplace_back(std::wstring(hp.hookcode)+L"=>"+text); } }); } catch (std::exception &e) { //std::wcout<size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size(); std::ofstream of; of.open("savehooks.txt"); for (auto line:*hooks) of<clear(); }).detach(); } Hooksearchsetting::Hooksearchsetting(mainwindow* p):mainwindow(p){ layout=new gridlayout(); SearchParam sp{}; spinduration=new spinbox(this,sp.searchTime); spinoffset=new spinbox(this,sp.offset); spincap=new spinbox(this,sp.maxRecords); spincodepage=new spinbox(this,Host::defaultCodepage); editpattern=new lineedit(this); editpattern->settext(tohex(sp.pattern,sp.length)); editmodule=new lineedit(this); editmaxaddr=new lineedit(this); editmaxaddr->settext(addr2hex(sp.maxAddress)); editminaddr=new lineedit(this); editminaddr->settext(addr2hex(sp.minAddress)); spinpadding=new spinbox(this,0); editregex=new lineedit(this); start=new button(this,HS_START_HOOK_SEARCH); layout->addcontrol(new label(this,HS_SEARCH_PATTERN),0,0); layout->addcontrol(new label(this,HS_SEARCH_DURATION),1,0); layout->addcontrol(new label(this,HS_PATTERN_OFFSET),2,0); layout->addcontrol(new label(this,HS_MAX_HOOK_SEARCH_RECORDS),3,0); layout->addcontrol(new label(this,HS_CODEPAGE),4,0); layout->addcontrol(new label(this,HS_SEARCH_MODULE),5,0); layout->addcontrol(new label(this,HS_MIN_ADDRESS),6,0); layout->addcontrol(new label(this,HS_MAX_ADDRESS),7,0); layout->addcontrol(new label(this,HS_STRING_OFFSET),8,0); layout->addcontrol(new label(this,HS_HOOK_SEARCH_FILTER),9,0); layout->addcontrol(start,10,1); layout->addcontrol(editpattern,0,1); layout->addcontrol(spinduration,1,1); layout->addcontrol(spinoffset,2,1); layout->addcontrol(spincap,3,1); layout->addcontrol(spincodepage,4,1); layout->addcontrol(editmodule,5,1); layout->addcontrol(editminaddr,6,1); layout->addcontrol(editmaxaddr,7,1); layout->addcontrol(spinpadding,8,1); layout->addcontrol(editregex,9,1); setlayout(layout); setcentral(1000,600); } std::vector hexStringToBytes(const std::wstring& hexString_) { auto hexString=hexString_; strReplace(hexString,L" ",L""); strReplace(hexString,L"??",FormatString(L"%02hX",XX)); std::vector bytes; if (hexString.length() % 2 != 0) { return {}; } for (int i = 0; i < hexString.size() / 2; i++) { auto byteValue = std::stoi(hexString.substr(i*2,2), nullptr, 16); bytes.push_back(byteValue); } return bytes; } void Hooksearchsetting::call(std::setpids,std::wstring reg){ if(pids.empty())return; if(auto filename=gmf(*pids.begin())) editmodule->settext(std::filesystem::path(filename.value()).filename().wstring()); editregex->settext(reg); spincodepage->setcurr(Host::defaultCodepage); start->onclick=[&,pids](){ close(); SearchParam sp{}; sp.searchTime=spinduration->getcurr(); sp.offset=spinoffset->getcurr(); sp.maxRecords=spincap->getcurr(); sp.codepage=spincodepage->getcurr(); if(editpattern->text().find(L".")==std::wstring::npos){ auto hex=hexStringToBytes(editpattern->text()); memcpy(sp.pattern,hex.data(),hex.size()); sp.length=hex.size(); } else{ wcsncpy_s(sp.exportModule, editpattern->text().c_str(), MAX_MODULE_SIZE - 1); sp.length = 1; } wcscpy(sp.boundaryModule,editmodule->text().c_str()); sp.minAddress=std::stoll(editminaddr->text(), nullptr, 16); sp.maxAddress=std::stoll(editmaxaddr->text(), nullptr, 16); sp.padding=spinpadding->getcurr(); realcallsearchhooks(pids,editregex->text(),sp); }; show(); } Hooksearchwindow::Hooksearchwindow(LunaHost* host):mainwindow(host){ cjkcheck=new checkbox(this,SEARCH_CJK); hs_default=new button(this,HS_SEARCH_FOR_TEXT); hs_text=new button(this,HS_SEARCH_FOR_TEXT); hs_user=new button(this,HS_SETTINGS); layout=new gridlayout(); layout->addcontrol(cjkcheck,0,0,1,3); layout->addcontrol(hs_default,1,0); layout->addcontrol(hs_text,1,1); layout->addcontrol(hs_user,1,2); setlayout(layout); settext(BtnSearchHook); setcentral(800,200); auto dohooksearchdispatch=[&,host](int type) { close(); if(type==1) { if(hooksearchText==0) hooksearchText=new HooksearchText(this); hooksearchText->call(host->attachedprocess); return; } auto filter=(cjkcheck->ischecked()?L"[\\u3000-\\ua000]{4,}" : L"[\\u0020-\\u1000]{4,}"); if(type==0) { SearchParam sp = {}; sp.codepage = Host::defaultCodepage; sp.length = 0; realcallsearchhooks(host->attachedprocess,filter,sp); } else if(type==2) { if(hooksearchsetting==0) hooksearchsetting=new Hooksearchsetting(this); hooksearchsetting->call(host->attachedprocess,filter); return; } }; hs_default->onclick=std::bind(dohooksearchdispatch,0); hs_text->onclick=std::bind(dohooksearchdispatch,1); hs_user->onclick=std::bind(dohooksearchdispatch,2); }