《软件报》2008年第11期 邮发代号:61-74
QQ是我们很常用的聊天软件,当登录QQ时,一般都是在其登录窗口中输入QQ号码和密码,之后才可以进入聊天窗口。但是,现在很多木马程序往往通过各种手段来截取您的QQ密码,例如设置键盘钩子,伪造QQ登录界面等。虽然QQ提供了nProtect键盘加密保护技术,不过最新的QQ木马完全可以直接破解该保护技术,在QQ用户毫无察觉的情况下直接截取QQ密码信息。如何才能打破常规,实现QQ安全快速登录呢?
实际上,除了使用正常的QQ登录对话框实现登录操作外,还可以使用QQ命令行实现登录操作。QQ命令行的格式为“QQ.exe /START QQUIN:QQ号码 PWDHASH:加密后的QQ密码 /STAT:登录状态”,其中的“QQ.exe”表示QQ的完整程序路径,例如“C:\Program Files\Tencent\QQ.exe”,“QQUIN”参数后面跟随QQ号码,“PWDHASH”参数后面跟随加密后的QQ密码。在执行登录操作时,QQ根据取得您输入的QQ密码后,使用MD5散列算法对其进行加密处理,得到16个字节的MD5散列值,然后使用BASE64编码对该散列值进行后编码处理,之后得到的值才可以用于登录操作。“STAT”参数后跟登录状态值,其中“40”表示隐身登录,“41”表示正常登录。QQ主程序利用取得的QQ号码,加密后的密码以及登录状态参数。因此,只要取得了您的QQ密码的编码信息,就可以在CMD窗口中使用命令行安全登录QQ了。因为没有使用QQ的登录对话框,再狡猾的病毒也无法探知您的QQ密码信息。根据以上原理,这里就使用Delphi 设计一个实用的QQ命令行生成程序。
在Delphi 中创建一个新的项目,在表单中放置一个名称为“num”的编辑框,用于输入QQ号码,放置一个名称为“pass”的编辑框,用于输入QQ密码,放置一个标题为“隐身登录”的CheckBox控件,用于判断是否使用隐身登录,放置一个名称为“edit1”的编辑框,用于显示完整的QQ登录命令语句,放置一个名称为“button1”的按钮,用于产生QQ命令行。注意在程序开头的“Uses”栏中应该包含“IdHashMessageDigest”单元,它实际上包含在Delphi 7内置的Indy控件包中,这里要使用其中包含的HashValue函数计算QQ密码的16字节的MD5 散列值,然后再用BASE64编码对该散列值进行编码得到的所需的密码数据。该函数完整的格式为“function HashValue(AStream: TStream): T4x4LongWordRecord; override;”,其中的参数类型是一个流或者字符串对象,经过计算可以返回T4x4LongWordRecord类型的值。在窗体的创建事件中,取得QQ的安装路径。,相关的程序片段为:
…
with reg do //reg为Tregistry对象
begin
rootkey:=HKEY_LOCAL_MACHINE;
if openkey(’SOFTWARE\TENCENT\PLATFORM_TYPE_LIST\1’,true) then //使用openkey函数打开QQ在注册表中的相关主键
begin
qqpath:= ReadString(’TypePath’);//使用readstring函数读取QQ安装路径
end;
CloseKey;
Destroy;
end;
end;
…
程序中主要的部分是计算QQ密码的加密后的散列值,其具体程序为:
…
function GetCommandLine(QQNum, QQPw: string; QQState: integer): string;
type
TempChar = array[0..15] of char;//定义16个字节的数组,用于保存MD5散列值
var
md5: TIdHashMessageDigest5;//定义一个Hash散列对象,其中的TIdHashMessageDigest5 类是在 IdHashMessageDigest 单元中声明的
begin
md5 := TIdHashMessageDigest5.Create;//创建一个Hash散列对象
result := ’ /START QQUIN:’ + QQNum + ’ PWDHASH:’ + Base64(TempChar(md5.HashValue(QQPw))) + ’ /STAT:’ + IntToStr(QQState); //使用MD5加密算法计算QQ密码的散列值,之后使用自定义的Base64()函数对其进行编码,之后将使用预设格式组合QQ号码,加密后的密码以及隐身登录标记,之后将将HashValue函数的值转换成字符集
md5.Free; //释放MD5散列对象
end;
…
当点击“button1”按钮时,产生完整的QQ登录命令行,如果勾选“隐身登录”项,表示使用隐身登录模式,否则的话,表示使用正常登录模式。在“edit1”中即可显示不同模式下的完整命令行。例如“"C:\PROGRA~1\Tencent\QQ\QQ.exe" /START QQUIN:2899802 PWDHASH:SAPAtFJ97zat6d6vB9Sw/A== /STAT:41”,可以看到QQ密码已经被加密处理过了,这里使用的是正常的登录模式。当然,这里的QQ号码和加密登录密码是假设的。此外,当点击了“button1”按钮后,同时还会在该程序的运行路径中创建一个名为“secureQQ.bat”的批处理文件,其中包含完整的QQ登录命令行,之后双击该批处理文件,即可快速登录到QQ上了。别人即使得到该文件,也无法得到真实的QQ密码。只要您的QQ密码设置的复杂,利用MD5算法加密过的密码几乎无法破解的。相关的命令行为:
…
procedure TForm1.Button1Click(Sender: TObject);
var
F: TextFile;
fn: string;
begin
if num.Text=’’ then
begin
Messagebox(handle,’请输入您的QQ号码!’,’提示’,mb_OK);
exit;
end;
if pass.Text=’’ then
begin
Messagebox(handle,’请输入您的QQ密码!’,’提示’,mb_OK);
exit;
end;
if checkbox1.Checked then
begin
edit1.Enabled:=true;
edit1.Text:=qqpath+’ ’+GetCommandLine(num.Text ,pass.Text ,40);//将QQ主文件路径和参数信息组合在一起,使用GetCommandLine函数计算QQ密码的散列值,如果Checkbox1处于选择状态,表示使用隐身登录
end
else //否则表示正常登录
begin
edit1.Enabled:=true;
edit1.Text:=’"’+qqpath+’"’+’ ’+GetCommandLine(num.Text ,pass.Text ,41);
end;
fn:=ExtractFilePath(paramstr(0))+’\’+’secureQQ.bat’;//创建批处理文件,将完整的QQ命令行写入该文件中
if fileexists(fn) then
DeleteFile(fn);
AssignFile(F, fn);
Rewrite(F);
writeln(F, ’@Echo off’);
writeln(F, edit1.text);
CloseFile(F);
end;
完整的源代码为:(该程序在Delphi7中调试通过)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,IdHashMessageDigest, StdCtrls,Registry, ExtCtrls;
type
TForm1 = class(TForm)
num: TEdit;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
pass: TEdit;
Edit1: TEdit;
Label1: TLabel;
CheckBox1: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure numKeyPress(Sender: TObject; var Key: Char);
procedure passKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
public
{ Public declarations }
end;
const
fSeedA = 831022 ;
fSeedB = 8310 ;
fKey=1983 ;
var
Form1: TForm1;
qqpath:string;
implementation
{$R *.dfm}
function Base64(Src: string): string;//Base64编码生成函数
const
DataSet = ’ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/’;
var
i, ModLen: integer;
Current: string;
Buf: array[1..3] of Byte;
NewBuf: array[1..4] of Byte;
begin
result := ’’;
if Src = ’’ then
exit;
ModLen := Length(Src) mod 3;
while Length(Src) > 0 do
begin
FillChar(Buf, 3, #0);
Current := Copy(Src, 1, 3);
Src := Copy(Src, 4, Length(Src) - 3);
for i := 1 to 3 do
Buf[i] := Ord(Current[i]);
NewBuf[1] := Buf[1] shr 2;
NewBuf[2] := (Buf[1] shl 6 shr 2 or Buf[2] shr 4) and $3F;
NewBuf[3] := (Buf[2] shl 4 shr 2 or Buf[3] shr 6) and $3F;
NewBuf[4] := Buf[3] and $3F;
for i := 1 to 4 do
result := result + DataSet[NewBuf[i] + 1];
end;
if ModLen >= 1 then
result[Length(result)] := ’=’;
if ModLen = 1 then
result[Length(result) - 1] := ’=’;
end;
function GetCommandLine(QQNum, QQPw: string; QQState: integer): string;
type
TempChar = array[0..15] of char;
var
md5: TIdHashMessageDigest5;
begin
md5 := TIdHashMessageDigest5.Create;
result := ’ /START QQUIN:’ + QQNum + ’ PWDHASH:’ + Base64(TempChar(md5.HashValue(QQPw))) + ’ /STAT:’ + IntToStr(QQState);
md5.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
F: TextFile;
fn: string;
begin
if num.Text=’’ then
begin
Messagebox(handle,’请输入您的QQ号码!’,’提示’,mb_OK);//如果QQ号码为空,弹出提示信息
exit;
end;
if pass.Text=’’ then
begin
Messagebox(handle,’请输入您的QQ密码!’,’提示’,mb_OK); //如果QQ密码为空,弹出提示信息
exit;
end;
if checkbox1.Checked then
begin
edit1.Enabled:=true;
edit1.Text:=qqpath+’ ’+GetCommandLine(num.Text ,pass.Text ,40);
end
else
begin
edit1.Enabled:=true;
edit1.Text:=’"’+qqpath+’"’+’ ’+GetCommandLine(num.Text ,pass.Text ,41);
end;
fn:=ExtractFilePath(paramstr(0))+’\’+’secureQQ.bat’;
if fileexists(fn) then
DeleteFile(fn);
AssignFile(F, fn);
Rewrite(F);
writeln(F, ’@Echo off’);
writeln(F, edit1.text);
CloseFile(F);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
reg:TRegistry;
begin
reg:=TRegistry.Create;
with reg do
begin
rootkey:=HKEY_LOCAL_MACHINE;
if openkey(’SOFTWARE\TENCENT\PLATFORM_TYPE_LIST\1’,true) then
begin
qqpath:= ReadString(’TypePath’);
end;
CloseKey;
Destroy;
end;
end;
procedure TForm1.numKeyPress(Sender: TObject; var Key: Char);
begin
if not (Key in [’0’..’9’,#8,#13]) then Key:=#0;// 判断QQ号码是否为数字
if key=#13 then
pass.SetFocus;// 当点击回车键时,转移输入焦点
end;
procedure TForm1.passKeyPress(Sender: TObject; var Key: Char);
begin
if key=#13 then
Button1.Click();
end;
end.
|