逆向小白之解決Focusky的離線登入問題
前言
Focusky是一款非常優秀的演示製作工具,類似於Prezi,但功能更強大,對中文的支援更好。所以已經成了我做演示的第一選擇,但有一個問題一直困擾我:公司使用的內部網路是不能連線到網際網路的,而Focusky的匯出等功能必須要登入才能使用,這就給我的講義的分享造成了障礙。培訓部門一直要求我把講義分享出來,我每次發個PDF稿過去,結果都被批評為不願分享。於是就想找個辦法解決一下這個問題。
提示:以下內容僅供技術學習參考,請支援優質國產正版軟體。
一、初步思路
最開始想到的思路是抓包分析,然後在內網上搭一個服務模擬響應,從而解決內網登陸問題,但是想想覺得這個方案難以實現,內網管理太嚴格,不可能隨便讓你架個服務的,還要修改DNS之類,在本機搭也很麻煩。(其實還是自己不太會 _)
於是決定還是逆向破解一下好了,把需要登入驗證的地方繞開就好了呀。於是按這個思路開動。
二、逆向嘗試
1.初探
先祭出x64dbg,準備對Focusky.exe 和 fs.exe進行跟蹤,跟了好一會才發現不太對勁,搞了半天fs.exe不是正常的可執行檔案。仔細觀察了一番,發現了Adobe AIR的資料夾,原來這是用Adobe AIR技術開發的ActionScript3應用,就是俗稱Flash的東東。
檢視了fs.exe的檔案頭,發現了CWS的字樣,這不就是swf檔案麼。看樣子要用到SWF逆向了。
2.再探
SWF逆向對我這個小白來說也是新東西,當然是先搜啊搜啊的呀,下了不少所謂的各種工具,最後發現還是JPEXS Free Flash Decompiler 最管用。這個軟體很良心,完全開源,功能強勁。
把fs.exe字尾改成swf,用FFDec開啟,果然是swf的喔。但是跟想象中不一樣的,沒有發現什麼資源和程式碼,只有一個DefineBinaryData和一些少量的指令碼。
後來在反編譯的指令碼里看到:
private function init() : void
{
context = new LoaderContext();
context.allowCodeImport = true;
loader = new Loader();
loader.contentLoaderInfo.addEventListener("complete",onComplete);
loader.contentLoaderInfo.addEventListener("ioError",onError);
loader.loadBytes(new FocuskyAirContentData(),context);
}
果然又巢狀了一層。
3.三探
於是把DefineBinaryData匯出成一個swf檔案再開啟,這下一切都出現了。指令碼和資源都一目瞭然了。
這樣很快就可以找到匯出EXE的PublishWindow的指令碼位置,可以找到幾個判斷的地方,比如:
對應的P-Code是:
這時只要點選edit p-code按鈕就可以對這個p-code修改了,改成iffalse ofs0059就可以實現不用登陸也可以了。
我以為只要儲存這個改過的SWF檔案,然後匯入fs.exe(實際也是swf檔案)就應該可以了。事實證明我太天真了,做完這些,再啟動Focusky就啟動不了啊。囧
不知為什麼會這樣,難道也有什麼自校驗之類的?還是FFDec修改儲存的SWF檔案有錯誤?總之該怎麼辦呢?
4.初捷
後來就想啊,這些檔案不是都要載入到記憶體來執行的麼,改檔案不行那改記憶體行不行呢?試試唄
又祭出了x64dbg,載入後在記憶體中搜尋對應的P-Code的hexdata
找到了一個唯一的地址,把修改後的P-code程式碼替換,這裡只改一個位元組就行了。
然後再執行試一下,奏效!不再提示需要登陸了!
當然,還不是很完美,因為會彈出要升級的視窗。不過這個思路已經通了,只要再找到類似的判斷點改掉就行了。
很快記憶體中需要修改的地方就都就位了,接下來就是要考慮怎麼打這個記憶體補丁了,畢竟不能每次都祭出x64dbg麼,還是需要簡單點才好啊。
5.試手
打記憶體補丁我只會一種方法,那就是用Dll劫持。試了一下version.dll,果然可以利用。於是用上Aheadlib,開啟VS,開始寫程式碼呀。
與固定記憶體地址的打補丁稍有區別的是,由於每次執行記憶體中這個SWF檔案的載入地址都是變化的,所以需要先搜尋指定的特徵碼,找到後再改。特徵碼當然就是P-Code對應的hexdata了,查詢演算法本來想試一試自己實現一下sunday的演算法,不過後來發現了一個更簡單的方法。
這個程式碼段所在的記憶體區域的大小是固定的,雖然地址每次都變,但大小一直不變。並且需要修改的地方相對於這個區域的偏移是固定的。這樣就只需要搜尋這個大小的記憶體區域,屬性為PRV、RW的就可以直接打補丁了。
以下是部分程式碼:
DWORD WINAPI PatchThread()
{
DWORD dwProcessId;
dwProcessId = GetCurrentProcessId();
HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS,false,dwProcessId);
ULONGLONG dwAddrStart = 0x0;
ULONGLONG dwAddrEnd = 0x7FFFFFFFFFFF;
ULONGLONG dwAddrCur = dwAddrStart;
ULONGLONG dwOffset = 0x3409F4;
MEMORY_BASIC_INFORMATION mbi;
memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION));
while(true){
dwAddrCur = dwAddrStart;
while(dwAddrCur<dwAddrEnd){
if (VirtualQueryEx(hProcess, (LPCVOID)dwAddrCur, &mbi, sizeof(mbi)) > 0)
{
if (MEM_COMMIT == mbi.State && MEM_PRIVATE == mbi.Type &&
PAGE_READWRITE == mbi.Protect &&
mbi.RegionSize == 0x17B0000//the region size that include the code to patch
)
{
//find it, do patch
ULONGLONG AddrPatch;
AddrPatch = (ULONGLONG)mbi.BaseAddress+dwOffset;
if(CheckTheMem(){
DoPatch();
Debug("Patch is ok! \n");
return 0;
}
return 0;
}else{
dwAddrCur += mbi.RegionSize ;
}
}else{
dwAddrCur=dwAddrStart;
Debug("VirtualQueryEx error:%d",GetLastError());
}
}
Debug("All memory is checked. \n") ;
Sleep(200);
}
return 0;
}
// 入口函式
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)PatchThread,NULL,0,NULL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}
return TRUE;
}
很快這個用於打補丁的version.dll就寫好了,放到Focusky的目錄,試一下。正常啟動,試著匯出exe,不會提示需要登陸了,終於可以離線也可匯出EXE了。成功!
6.再戰
本來到這裡我的初始目標已經實現了,解決了Focusky的離線使用問題。但是,呵呵,人都是有點好奇心的麼。我就想啊,我能不能乾脆讓它登入好了,就是離線也能識別成登入狀態,這樣其他需要登入的功能不是也能使用了不是麼。
於是仔細琢磨了一下,發現好像是可以實現的啊,只要把login的指令碼改動一下,把需要聯網獲取的使用者狀態變成本地獲取不就可以了麼。
於是就開始動手實踐了,當然過程比較漫長啦。畢竟我對ActionScript一點都不熟,再加上沒辦法直接寫AS程式碼,而是需要寫P-Code的程式碼,就是哪些Push,setlocal 的東西,所以還是花費了一定的時間。最後的使用者登入程式碼大概改成類似這個樣子:
public function login(u:String, p:String) : void
{
_logined = true;
var file:File = File.applicationDirectory.resolvePath("user.xml");
stream = new FileStream();
stream.open(file,"read");
data= new XML(stream.readUTFBytes(stream.bytesAvailable));
readData(data);
stream.close();
Global.focusky.gSignalManager.signaled("SIGIN_IN_SUCCESS",this);
}
當然這是用AS3程式碼示意的,實際是一堆類似下面的東西:
getlocal_0
pushscope
newactivation
dup
setlocal_3
pushscope
getlocal_0
getlocal_3
swap
setslot 3
findproperty Qname(ProtectedNamespace("com.wonderidea.focusky.air.user:User"),"_logined")
swap
user.xml是一個XML格式的文字,這個文字里麵包含了使用者的基本資訊,格式當然是抓的我自己的登入使用者資訊。示例如下:
<ID>4582165</ID>
<Email>master@focusky.com</Email>
<userName>master</userName>
…………
這些修改還是一樣通過version.dll來打記憶體補丁。最後的結果也是不錯的,可以保持和正常聯網登入一樣的效果。當然那些需要聯網使用的資源是用不了的。
總結
整個解決離線登入問題的過程還是很有意思的,第一次接觸了ActionScript,感覺這還真是個好東東。只是已經很小眾了,有點可惜。
這種打補丁的方式有一個不穩定的弊端,那就是有可能你打補丁的修改和原程式的讀取同時發生,這時就會出錯了,程式表現得很怪異。所以我通常需要把Focusky的自動登入關掉,然後把打補丁的時間延後到程式穩定啟動後。在我的機器上是延後了500ms左右。這樣基本上算是比較穩定。
最後,本文只為解決自己使用的痛點。請大家支援國產優質軟體。
寫作不易,如果覺得不錯的,請關注評論一下。
相關文章
- SSH登入很慢問題的解決
- Gradio離線部署到內網,資源載入失敗問題(Gradio離線部署問題解決方法)內網
- 微信登入-6問題解決方案
- 解決Ubuntu下MySQL遠端登入問題UbuntuMySql
- mysql登入遇到ERROR 1045問題解決方法MySqlError
- 記錄一個 gitlab 登入問題解決Gitlab
- 解決Windows安全性登入彈框的問題Windows
- [20210518]ssh ip登入緩慢問題解決.txt
- YII2自動登入Cookie失效問題解決Cookie
- oracle程式異常中止時登入掛起問題的解決Oracle
- 解決csdn登陸複製的問題
- 筆記:啟動登入HOMESTEAD、建立應用、解決問題筆記
- 離職,問題就解決了嗎?
- 一個ssh無法遠端登入的問題跟蹤解決
- 解決 PBootCMS 後臺登入不顯示驗證碼的問題boot
- 前後端分離使用 Token 登入解決方案後端
- Python小白的爬蟲問題與解決(含程式碼)Python爬蟲
- Ubuntu16.04使用者登入介面死迴圈問題的解決Ubuntu
- 解決微信小程式登入與釋出的一些問題微信小程式
- 由Linux核心bug引起SSH登入緩慢問題的排查與解決Linux
- 解決ZBLOG PHP 程式無法登入後臺賬戶問題PHP
- 如何解決網站登入後反爬的問題?網站
- 如何解決MacOS無法登入app store的問題?MacAPP
- 解決Dcat Admin laravel框架登入報錯問題,(blocked:mixed-content)Laravel框架BloC
- 前後端分離解決跨域問題後端跨域
- 解決docker jenkins 配置SSH免密登入配置成功後不生效問題DockerJenkins
- 解決代理連線超時問題
- Matlab解決線性規劃問題Matlab
- 小白的MyBatis逆向工程MyBatis
- DELPHI四捨五入問題解決
- strace解決sqlplus登陸緩慢的問題一例SQL
- pipenv + 離線移植專案 遇到的問題
- 這種方式解決EMC儲存崩潰RAID離線問題,簡單又高效AI
- 代理伺服器的連線問題及解決伺服器
- epic登入一直轉圈怎麼辦 快速解決epic客戶端無法登陸的問題客戶端
- 解決建立SpringBoot工程載入較慢的問題Spring Boot
- 解決 Laravel 5.8 前後端分離跨域問題Laravel後端跨域
- 修改labelme原始碼,解決粘連mask分離問題原始碼