扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:论坛整理 来源:zdnet安全频道 2008年12月16日
关键字: Foxmail
问题的出现与分析
最近我买了一支爱国者的经典型“迷你王”闪存,自己安装了 Foxmail ,并在闪存上创建了两个账号以收发邮件。但当我在另一台 PC 上使用 Foxmail 时,它却提示没有可用的账号。这是怎么回事呢?原来, Foxmail 在创建账号时,会将这个账号所在目录完整的路径名记录下来。由于闪存在不同的 PC 上获得的盘符不一定相同,这样在闪存的盘符改变后, Foxmail 便因无法定位该目录而发生错误。
开始时,我编辑了一个批处理文件 Foxmail.BAT 用以启动 Foxmail 。其思路是将闪存的根
目录重定向为 Z: 盘,每次将闪存的当前盘符作为参数传递给这个批处理文件即可。为此我不得
不将之前创建的账号删除,在命令行提示符下启动批处理命令,然后在 Z: 盘下创建账号。这样
不是太麻烦了吗?是的!我也曾尝试过利用中间文件自动进行参数传递,但是由于 DOS
Foxmail 的账号存储机制
Foxmail 将每个账号的最基本信息(账号名称、存储目录)存放在安装目录下的文件
Accounts.CFG 中。在每个账号的目录中则利用文件 Account.STG 存储该账号的其他信息。
Accounts.CFG 是一个复合文档,它包括一个 Ver30 存储( Storage )。在 Ver30 存储下有一个accounts.cfg 流( Stream )。你可以使用 Visual Studio 6.0 提供的 DocFile 阅读器打开它。关于存储和流的更多知识,请参阅 MSDN: [Platform SDK] Structured Storage 。
偏移地址 |
变量名 |
描述 |
00000000 |
BYTE Reserved [40]; |
文件头。在 00000007 处存储了曾创建账号的个数。 |
00000040 |
DWORD cAccount; |
现有的账号数。 |
第一个账号信息块( AIB , Account Information Block )的开始。 | ||
00000044 |
DWORD idxAccount; |
该账号的顺序编号。 |
DWORD lenACTName; |
账号名字符串的长度。 | |
String strACTName [lenACTName]; |
账号名字符串。 | |
DWORD lenACTPath; |
账号所在目录的字符串长度。 | |
String strACTPath [lenACTPath]; |
账号所在目录的全路径名。 | |
BYTE Reserved [18]; |
0x18 个 00 (可能用以存储密码)。 | |
下一个 AIB | ||
… | ||
注意:所有的字符串长度均不包括结尾符在内。 |
编程实现
现在我们就可以开始编写 Foxmail 的辅助工具 SmartFox 了。 重要约定:
SmartFox 是一个利用 MFC 实现的基于对话框的应用程序,静态链接 MFC 的动态链接库。
运行界面如下,单击左键启动 Foxmail ,单击右键退出。
(一)对程序中使用的 API 的介绍,更详细的内容请参见 MSDN :
1. 获得当前目录的全路径名 (Win API) DWORD GetCurrentDirectory(
2. 打开一个复合文档 (Win API)
DWORD nBufferLength, // 保存目录名的缓冲区大小
LPTSTR lpBuffer // 指向缓冲区的指针
);HRESULT StgOpenStorage(
3. 打开一个子存储或流 (IStorage API)
const WCHAR *pwcsName, //复合文档的文件名
IStorage *pstgPriority, //先前已打开的根存储的指针
DWORD grfMode, //访问模式
SNB snbExclude, //指向一个SNB结构的指针,以确定哪些元素将被排除访问
DWORD reserved, //保留
IStorage **ppstgOpen //接受返回的IStorage接口指针
);HRESULT OpenStorage(
OpenStream 的参数与 OpenStorage 的差不多,只是返回的是一个 IStream 指针。
const WCHAR *pwcsName, //要打开的存储的名字
IStorage *pstgPriority, //该参数一定为NULL值
DWORD grfMode, //访问模式
SNB snbExclude, //该参数一定为NULL值
DWORD reserved, //保留
IStorage **ppstg //接受返回的IStorage接口指针
);
HRESULT Stat(
5. 加载外部程序 (Win API)
STATSTG *pstatstg, //指向一个STATSTG结构的指针,STATSTG的cb域即为流的大小。
DWORD grfStatFlag //决定是否要在STATSTG中返回某些值的标志
);
HRESULT Seek(
LARGE_INTEGER dlibMove, //相对于dwOrigin的偏址
DWORD dwOrigin, //起始位置
ULARGE_INTEGER *plibNewPosition //接受指向新位置的指针
);
HRESULT Read(
void *pv, //指向数据缓冲区的指针
ULONG cb, //要读的字节数
ULONG *pcbRead //实际读出的字节数
);Write 的参数与 Read 的一样。HINSTANCE ShellExecute(
HWND hwnd, //父窗口句柄
LPCTSTR lpVerb, //要执行的动作:edit,explore,find,open,print,properties
LPCTSTR lpFile, //文件名
LPCTSTR lpParameters, //传递的命令行参数
LPCTSTR lpDirectory, //缺省工作目录
INT nShowCmd //窗口的显示模式
);
UINT WinExec(
LPCSTR lpCmdLine, // 命令的字符串
UINT uCmdShow //窗口的显示模式
);HRESULT CoGetMalloc( //这是一个Win API
IMalloc::Alloc() ,IMalloc::Free() 的使用与 C 语言中的 alloc() 和 free() 类似,在此不再赘述。
LPMALLOC * ppMalloc //接受返回的内存分配器的IMalloc接口指针
);
(二)实现步骤:
IStorage * m_pRootStg; // 根存储的接口指针
IStorage * m_pVer30Stg; //Ver30 存储的接口指针
IStream * m_pStream; //accounts.cfg 流的接口指针
char * m_pBuffer; // 用以读写 accounts.cfg 流的缓冲区指针
char m_Driver; // 闪存的当前盘符
CArray <ULONG, ULONG> m_aryPosition; // 保存流中账号目录所在偏移地址的数组
BOOL CSmartFoxDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
try
{
m_Driver = GetDriver();
m_pStream = GetIStream();
m_pBuffer = GetBuffer(m_pStream);
GetAccountInfo(m_pStream, m_pBuffer);
}
catch (char * sMsg)
{
AfxMessageBox(sMsg,MB_OK,NULL);
ClearUp();
}
return TRUE; // return TRUE unless you set the focus to a control
}
void CSmartFoxDlg::OnClickEmail()
{
ShellExecute(this->m_hWnd,
"open",
"mailto: korby@sohu.com?subject=Re: 关于SmartFox的意见",
NULL,
NULL,
SW_SHOWNORMAL);
ClearUp();
}
void CSmartFoxDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// I will modify the driver letter of accounts here, and startup FoxMail.
try
{
ModifyAccountDriver(m_pStream, m_pBuffer);
ClearUp();
if (WinExec("FoxMail.EXE", SW_SHOWNORMAL) < 31) throw "加载FoxMail.EXE失败";
}
catch (char * sMsg)
{
AfxMessageBox(sMsg,MB_OK,NULL);
}
}
void CSmartFoxDlg::OnRButtonDown(UINT nFlags, CPoint point)
{
// If I don''''t do this, the message will be transferred to window behind.
SetCapture();
}
void CSmartFoxDlg::OnRButtonUp(UINT nFlags, CPoint point)
{
::ReleaseCapture();
ClearUp();
}
void CSmartFoxDlg::ClearUp()
{
if (m_pRootStg != NULL) m_pRootStg->Release();
if (m_pVer30Stg != NULL) m_pVer30Stg->Release();
if (m_pStream != NULL) m_pStream->Release();
if (m_pBuffer != NULL)
{
IMalloc * pMalloc;
::CoGetMalloc(MEMCTX_TASK, &pMalloc);
pMalloc->Free(m_pBuffer);
pMalloc->Release();
}
::CoUninitialize();
OnCancel();
}
// All codes below find out driver letter and directories of each account.
char CSmartFoxDlg::GetDriver()
{
char sCurDir[256];
int ret = ::GetCurrentDirectory(256, sCurDir);
if (ret == NULL) throw "取当前驱动器盘符时失败";
else return sCurDir[0];
}
IStream * CSmartFoxDlg::GetIStream()
{
USES_CONVERSION;
// Get interface Storage pointer of Accounts.CFG
IStream * pStream;
HRESULT hr;
hr = ::StgOpenStorage(T2COLE("Accounts.CFG"),
NULL,
STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
NULL,
0,
&m_pRootStg);
if (hr != S_OK) throw "打开Accounts.CFG文件时失败";
hr = m_pRootStg->OpenStorage(T2COLE("Ver30"),
NULL,
STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
NULL,
0,
&m_pVer30Stg);
if (hr != S_OK) throw "打开Ver30存储时失败";
hr = m_pVer30Stg->OpenStream(T2COLE("accounts.cfg"),
NULL,
STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
NULL,
&pStream);
if (hr != S_OK) throw "打开accounts.cfg流时失败";
return pStream;
}
char * CSmartFoxDlg::GetBuffer(IStream * pStream)
{
STATSTG StatStg;
IMalloc * pMalloc;
char * pBuffer;
HRESULT hr;
hr = pStream->Stat(&StatStg, NULL);
if (hr != S_OK) throw "读取accounts.cfg流的大小时失败";
hr = ::CoGetMalloc(MEMCTX_TASK, &pMalloc);
if (hr != S_OK) throw "获取COM库的IMalloc接口指针时失败";
pBuffer = (char *)pMalloc->Alloc(ULONG(StatStg.cbSize.QuadPart));
if (pBuffer == NULL) throw "申请缓冲区时失败";
pMalloc->Release();
return pBuffer;
}
void CSmartFoxDlg::GetAccountInfo(IStream * pStream, char * pBuffer)
{
// I will find out names and directories of each account.
STATSTG StatStg;
ULONG cbReaded;
HRESULT hr;
char * p = pBuffer;
DWORD cAccount;
DWORD len;
CString name; // Gets the name of account.
CString path; // Gets the path of account.
CString strEdit; //Displays text in edit control.
CEdit * pEdit = (CEdit *) GetDlgItem(IDC_EDIT);
strEdit.Format("闪存当前为%c:盘, 现存账号及其目录: \r\n", m_Driver);
hr = pStream->Stat(&StatStg, NULL);
if (hr != S_OK) throw "读取accounts.cfg流的大小时失败";
hr = pStream->Read(pBuffer, ULONG(StatStg.cbSize.QuadPart), &cbReaded);
if (hr != S_OK) throw "读取accounts.cfg流的内容时失败";
p += 0x40;
cAccount = (*p); //Count of accounts.
p += 0x4;
for (DWORD i = 1; i <= cAccount; i++)
{
// Value of (*p) is index of account.
if (DWORD(*p) != i) throw "accounts.cfg流损坏";
p += 0x4; //Skips the index number.
len = DWORD(*p); //Gets length of name.
p += 0x4; //Skips the length number. The string does not include NULL.
name.Empty();
path.Empty();
for (DWORD n = 0; n < len; n++, p++) name += char (*p); // Gets account name.
len = DWORD(*p); // Gets length of directory.
p += 4; // Skips the length number.
m_aryPosition.Add(p-pBuffer); //Stores offset into array.
for (n = 0; n< len; n++, p++) path += char (*p); //Gets account path.
strEdit += name+"\t"+path+"\r\n";
p += 0x18; //Skips 0x18 Nulls.
}
pEdit->SetWindowText(strEdit);
}
void CSmartFoxDlg::ModifyAccountDriver(IStream *pStream, char * pBuffer)
{
STATSTG StatStg;
ULONG cbWrited;
HRESULT hr;
LARGE_INTEGER MovOffset;
ULARGE_INTEGER NewPosition;
char * p;
for (int i = 0; i< m_aryPosition.GetSize(); i++)
{
p = pBuffer + m_aryPosition.GetAt(i);
(*p) = m_Driver;
}
hr = pStream->Stat(&StatStg, NULL);
if (hr != S_OK) throw "读取accounts.cfg流的大小时失败";
MovOffset.QuadPart = 0;
hr = pStream->Seek(MovOffset, 0, &NewPosition);
if (hr != S_OK) throw "移动accounts.cfg流的读写指针时失败";
pStream->Write(pBuffer, ULONG(StatStg.cbSize.QuadPart), &cbWrited);
if (hr != S_OK) throw "向accounts.cfg流写入数据时失败";
}
编译链接生成 SmartFox.EXE 后,将其拷贝到 Foxmail 在闪存上的安装目录。利用 SmartFox.EXE 运行 Foxmail 就可以实现“随身邮”的功能了!
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。