某些應用程式將 SWF (Flash) 檔案內嵌於 EXE 資源中,或以特定格式附加於 EXE 尾端。本文提供一個實用函式 exe2swf,掃描 EXE 檔案尋找 FWS (SWF 標誌) 簽名,提取後續資料並存為獨立 .swf 檔案,支援 Flash 6 格式。
AnsiString exe2swf(AnsiString FileName)
{
int iFileHandle;
unsigned long iFileLength;
unsigned long iBytesRead;
AnsiString dFileName;
unsigned char* pszBuffer;
// 1. 開啟來源 EXE
iFileHandle = FileOpen(FileName.c_str(), fmOpenRead);
if (iFileHandle < 0) return NULL;
// 2. 取得檔案長度
iFileLength = FileSeek(iFileHandle, 0, 2); // 從末尾偏移 0
FileSeek(iFileHandle, 0, 0); // 回到開頭
// 3. 分配緩衝區讀入整個檔案
pszBuffer = new unsigned char[iFileLength + 1];
if (!pszBuffer)
{
FileClose(iFileHandle);
return NULL;
}
iBytesRead = FileRead(iFileHandle, pszBuffer, iFileLength);
FileClose(iFileHandle);
// 4. 產生目標 SWF 檔名 (同目錄、同主檔名、副檔名 .SWF)
dFileName = FileName.UpperCase().SubString(1, FileName.Length() - 4) + ".SWF";
// 5. 掃描尋找 "FWS" 簽名 (0x46 0x57 0x53)
// 注意:原碼邏輯為尋找前兩字節為 0, 0, 第三字節為 'F'...
// 這暗示 SWF 前有特定填充或標記,實際 SWF 檔案開頭即為 "FWS" 或 "CWS"
int i = 0, ii = 0;
for (i = 0; i < (int)iFileLength - 2; i++)
{
// 原碼條件:pszBuffer[i-2]==0 && pszBuffer[i-1]==0 && pszBuffer[i]=='F'...
// 這裡修正為標準 SWF 簽名搜尋:直接找 "FWS" 或 "CWS"
if (i + 2 < (int)iFileLength &&
pszBuffer[i] == 'F' && pszBuffer[i+1] == 'W' && pszBuffer[i+2] == 'S')
{
ii = i;
break; // 找到第一個即可
}
// 兼容壓縮 SWF (CWS)
if (i + 2 < (int)iFileLength &&
pszBuffer[i] == 'C' && pszBuffer[i+1] == 'W' && pszBuffer[i+2] == 'S')
{
ii = i;
break;
}
}
if (ii == 0 && !(pszBuffer[0]=='F' && pszBuffer[1]=='W' && pszBuffer[2]=='S'))
{
// 未找到簽名
delete[] pszBuffer;
return NULL;
}
// 6. 移動緩衝區指標至簽名位置
pszBuffer += ii;
unsigned long swfLength = iFileLength - ii;
// 7. 版本檢查 (原碼針對特定偏移值判斷 Flash 4/5/6)
// 這裡簡化:直接寫入,播放器自行處理版本相容
// if (ii != 376831 && ii != 286719 && ii != 811008) { ... }
// 8. 建立並寫入 SWF 檔案
iFileHandle = FileCreate(dFileName.c_str());
if (iFileHandle >= 0)
{
FileWrite(iFileHandle, pszBuffer, swfLength);
FileClose(iFileHandle);
}
// 9. 釋放原始緩衝區 (注意:pszBuffer 已偏移,需回推)
pszBuffer -= ii;
delete[] pszBuffer;
return dFileName;
}
| 簽名 | 十六進位 | 含義 |
|---|---|---|
| FWS | 46 57 53 | 未壓縮 SWF (Flash 6+) |
| CWS | 43 57 53 | Zlib 壓縮 SWF (Flash 6+) |
| ZWS | 5A 57 53 | LZMA 壓縮 SWF (Flash 11+) |
| 函式 | 對應 API | 說明 |
|---|---|---|
| FileOpen | CreateFile | fmOpenRead=0, fmOpenWrite=1, fmOpenReadWrite=2 |
| FileCreate | CreateFile (CREATE_ALWAYS) | |
| FileSeek | SetFilePointer | origin: 0=始, 1=當前, 2=末 |
| FileRead | ReadFile | |
| FileWrite | WriteFile | |
| FileClose | CloseHandle |
new unsigned char[size] 分配陣列,必須配對 delete[]。pszBuffer += ii 後寫入,寫入完成前必須回推 pszBuffer -= ii 才能正確 delete[]。std::vector 或 std::unique_ptr 避免手動管理錯誤。
std::vector ExtractAllSwfFromExe(AnsiString exePath)
{
std::vector results;
int hFile = FileOpen(exePath.c_str(), fmOpenRead);
if (hFile < 0) return results;
unsigned long len = FileSeek(hFile, 0, 2);
FileSeek(hFile, 0, 0);
std::vector buf(len);
FileRead(hFile, buf.data(), len);
FileClose(hFile);
const char* sigs[] = {"FWS", "CWS", "ZWS"};
for (unsigned long i = 0; i + 3 < len; ++i)
{
for (const char* sig : sigs)
{
if (buf[i] == sig[0] && buf[i+1] == sig[1] && buf[i+2] == sig[2])
{
// 找到 SWF 簽名,嘗試解析長度 (SWF Header 第 4-7 字節為檔案長度)
if (i + 8 <= len)
{
DWORD swfLen = *(DWORD*)(buf.data() + i + 4); // Little-endian
if (swfLen > 0 && swfLen <= len - i)
{
AnsiString outName = exePath + "_swf_" + IntToStr(results.size()) + ".swf";
int hOut = FileCreate(outName.c_str());
if (hOut >= 0)
{
FileWrite(hOut, buf.data() + i, swfLen);
FileClose(hOut);
results.push_back(outName);
}
i += swfLen - 1; // 跳過此 SWF
break;
}
}
}
}
}
return results;
}
提取內嵌 SWF 可能涉及:
僅在合法授權、安全研究、個人合理使用範圍內使用此技術。
掌握二進位檔案掃描與格式簽名識別技巧,不僅能提取 SWF,同樣適用於 PNG、JPG、ZIP、PE 等任何已知魔數的檔案格式提取與分析。