官术网_书友最值得收藏!

第1章 黑客編程入門

你是否曾經在用別人開發的工具嘗試“入侵”,你是否希望開發出自己的黑器……相信很多人有著這種近似相同的經歷。本章將簡單介紹黑客編程及工具開發。如果你是初學編程,如果你從來沒有接觸過黑客軟件的開發,如果你急于想了解黑客編程方面的知識……那么就請繼續往下閱讀。

1.1 編程語言和開發環境的選擇

初學者剛開始學習編程語言最頭疼的問題就是如何選擇編程語言及合適的開發環境,下面就來具體介紹一下。

有人認為學編程就是學編程語言,而VC、VB這樣的開發環境只是工具,不需要學。這個想法是錯誤的,因為開發環境提供了很多開發工具,如VC這個集成開發環境就提供了與之對應的PSDK、MFC等。除了語言以外,要開發特定的軟件是需要開發包和開發工具支持的。況且,編程語言也是一種工具,用于和計算機進行交流的工具。所以我們既要學習編程語言,也要學習開發工具。

對于選擇哪種編程語言或者開發環境其實也沒有特定的標準。有這樣一句話,“真正的程序員用VC,聰明的程序員用Delphi,用VB的不是程序員”。筆者卻并不這么認為,因為在很多編程的書籍上常常這樣提醒并告誡學習者,編程的精髓是“算法”,而語言是用來描述算法的。因此,大家也不必因為無法選擇而無從下手。

黑客一般都掌握多種編程語言,他們不但掌握著與底層相關的如匯編、C之類的編程語言,而且還掌握很多腳本語言,如Python、Perl、Ruby……很多黑客在發現0Day以后用Perl或者Python來寫POC;MSF使用的是Ruby來進行開發Exploit;有的黑客在反病毒時竟然寫個批處理就搞定了……對于黑客來說,一切語言都是服務于自己的思想的,只要能快速實現自己的想法,能完成自己所要完成的功能就行,從不拘泥于任何語言和工具。在網上有很多學習不同編程語言的人們之間經常互相攻擊,這其實是一種極端的行為,大家還是理性地對待這些問題比較好。

前面說了這么多,仿佛是在繞圈子,一直沒有介紹到底應該選擇什么編程語言和開發環境。我們這里選擇使用C語言作為黑客編程的學習語言,選擇VC6(VisualC++ 6.0的縮寫)來作為我們的開發環境。VS 6相對于Visual Studio 2005、Visual Studio 2008和Visual Studio 2010之類的開發環境來說要小巧很多,當前是可以滿足我們的開發需求的。選擇C語言的原因是由于Windows的API都是用C語言定義的,相對于使用其他編程語言會方便很多。筆者認為在VB下使用API就非常不方便,尤其是涉及指針這個概念的時候。除了VC6以外,還需要下載新版的PSDK,因為VC6中包含的PSDK過于舊,有些新的API我們無法通過包含頭文件而直接使用,因此這個也是必須的。

1.1.1 何為SDK、API和MFC

既然選擇VC作為開發環境,那么先來了解一下VC開發環境中今后會用到的一些工具的概念,這些概念都相對比較簡單,常見的概念有SDK、API和MFC。

SDK是Software Develop Kits的縮寫,即軟件開發工具包。SDK是一個泛指,比如對視頻采集卡進行二次開發,那么視頻采集卡會提供SDK;再比如對動態令牌進行二次開發,那么動態令牌也會提供SDK。操作系統為了程序員在其平臺下開發應用程序也會提供SDK,我們對系統提供的開發包稱之為PSDK。PSDK是Platform SDK的意思,也就是平臺SDK。對于我們來說,平臺就是Windows操作系統。Windows下的PSDK包含了進行Windows軟件開發的文檔和API函數的輸入庫、頭文件等一些與Windows開發相關的工具。PSDK是一個單獨的開發包,不過每個不同版本的VC和其他一些開發環境中也已經包含了它。

API是Application Programming Interface的縮寫,即應用程序接口。不同的SDK提供不同的API。PSDK提供的API就是操作系統提供的開發應用程序的接口,比如Windows系統下刪除文件的API函數是DeleteFile();再比如Windows系統下移動文件的API函數是MoveFile(),而其他一些供二次開發的SDK中附帶的API,也是為了進行開發應用程序而提供的接口。

MFC是Microsoft Foundation Class的縮寫,即微軟基礎類庫。它是微軟為了簡化程序員的開發工作量所提供的基于C++類的一套面向對象的庫,它封裝了常見的API函數,使得開發較為方便。

我們在書中會用到API進行開發,也會使用MFC進行開發。不過對于MFC的使用,基本上用在與界面相關的部分,一般是簡單地帶過,不會進行過多的討論。我們的重點是放在API函數的使用上。關于MFC的相關內容,還請大家自行參考學習。

1.1.2 VC6和SDK的配置

新版的PSDK(Windows Server 2003 SP1 Platform SDK)的下載地址為http://www.microsoft.com/downloads/en/details.aspx?FamilyID=eba0128f-a770-45f1-86f3-7ab010 b398a3。如果此地址過期的話,請大家在網上自行搜索并下載。

SDK和VC6互相是獨立的,不需要安裝在同一個目錄下,根據自己的實際情況安裝就可以了。在安裝好VC6和新版的SDK后,需要在VC6中進行相應的設置才能使用新版的SDK,否則VC6仍然使用其自帶的舊的SDK。SDK和VC6的安裝步驟這里就不介紹了(提示:請把VC6安裝完整,VC6會提供一些代碼,對我們的學習是非常有幫助的),下面介紹新版的SDK如何配置才能在VC6中使用。

啟動VC6,單擊菜單“Tools”->“Options”命令,打開“Options”對話框,如圖1-1所示。

選擇“Directories”選項卡,在“Show directories for”下拉列表中選擇“Include files”,選項并在“Directories”列表框中添加新的PSDK頭文件的目錄,放在列表的最上面,如圖1-2所示。

圖1-2 頭文件的路徑

在“Show directories for”下拉列表中選擇“Library files”選項,并在“Directories”列表框中添加新的PSDK庫文件的目錄,放在列表的最上面,如圖1-3所示。

圖1-3 庫文件的路徑

切記要把所添加的目錄放到列表的最上邊,因為在VC編譯代碼的時候會搜索這些目錄里的文件,如果隨便放,編譯器會因找不到相關API函數定義而報函數未定義的錯誤。

另外,還必須下載一個MSDN。MSDN即Microsoft Developer Network,它是微軟開發的聯機幫助文檔,可以幫助我們在使用API的時候進行快速的查閱,以方便我們對API的使用和理解。但是MSDN里的內容全部都是英文的,如果你英文不太好可以借助搜索引擎來學習API的使用。本書只對所提到的API函數常用的參數進行介紹,其他參數需要大家自行進行學習。

1.2 應用程序的調試

在開發程序的過程中,除了編碼以外還需要對程序進行調試,當編寫的程序出現問題后,就要對程序進行調試。調試不是僅使用一個printf()或MessageBox()進行簡單的輸出來觀察某個函數的返回值(雖然在調試的時候的確是對返回值觀察較多),也不是對某個變量、某一時間的具體值的輸出。調試是有專業的調試分析工具的,VC6不但提供代碼編輯、代碼編譯、編譯連接等功能,還提供了一個非常好用的調試工具。在編寫完代碼后,如果程序輸出的結果是未知的,或者是沒有預測到的,都可以通過調試來對代碼的邏輯進行分析,以找到問題的所在。掌握調試的技能,對軟件的開發有非常大的幫助。掌握好的調試工具,對于調試者來說,也同樣會起到事半功倍的作用。下面通過一個簡單的例子了解一下VC6提供的調試功能吧。

1.2.1 編寫我們的第一個程序

下面介紹用VC6寫一個控制臺版的HelloWorld來學習VC6的開發。也許大家認為這個程序很簡單,但是請記住,我們的重點是要介紹VC6這個集成開發環境中提供的調試功能。

啟動VC6,單擊菜單“File”->“New”命令,在彈出的對話框中選擇“Projects”選項卡,然后在左邊的列表框中選擇“Win32 Console Application”選項,在“Project Name:”文本框中填寫“HelloWorld”,如圖1-4所示。

圖1-4 “Projects”選項卡

單擊“OK”按鈕,出現如圖1-5所示窗口。

圖1-5 “Win32 Console Application”項目向導

選擇“An empty project”單選項,單擊“Finish”按鈕,然后在彈出的對話框中單擊“OK”按鈕。

單擊菜單“File”->“New”命令,選擇“Files”選項卡,在左邊的列表中選擇“C++ Source File”選項,在右邊的“File”文本框中填寫“Hello World”,如圖1-6所示。

圖1-6 “Files”選項卡

單擊“OK”按鈕就可以進行代碼編輯了。

在代碼編輯處錄入如下代碼:

#include <stdio.h>
int main()
{
 printf("Hello World ! \r\n");
 return 0;
}

按F7鍵進行編譯連接(按Ctrl+ F7組合鍵是只編譯不進行連接),按Ctrl + F5組合鍵進行運行,如圖1-7所示。

圖1-7 “Hello World”運行界面

這就是我們值得紀念的第一個程序。這個程序很簡單,有C語言基礎的讀者應該都能看懂,這里就不進行介紹了。如果看不懂,請先找本關于C語言入門的書學習一下。

1.2.2 用VC6調試第一個程序

現在來學習如何使用VC6對第一個程序進行調試。在代碼編輯狀態下,按下鍵盤上的F10鍵,進入調試狀態,如圖1-8所示。

圖1-8 VC6處于調試狀態

常用的調試窗口有兩個,一個是“Watch”窗口(標注“1”的那個窗口),一個是“Memory”窗口(標注“2”的那個窗口)。打開“Watch”窗口的方法是單擊“View”->“Debug Windows”->“Watch”命令(或按Alt +3組合鍵)打開。打開“Memory”窗口的方法是單擊“View”->“Debug Windows”->“Memory”命令(或按Alt+ 6組合鍵)打開。“Watch”窗口用來監視我們感興趣的變量,而當我們有時無法通過變量的值進行判斷時,就需要借助“Memory”窗口中的值,比如,指針的值來進行判斷。

除了這兩個窗口以外,還有“Call Stack”、“Register”和“Disassembly”這3個窗口,分別如圖1-9、圖1-10和圖1-11所示。

圖1-1 “Options”對話框

圖1-9 “CallStack”窗口

圖1-10 “Register”窗口

圖1-11 “Disassembly”窗口

“Call Stack”窗口是調用棧窗口,該窗口可以很方便地查看調用關系,很容易通過調用棧來找到上層、上上層的調用者。另外,也可以通過調用棧來定位錯誤。比如,有時程序會崩潰,但是發生崩潰的地方卻在系統提供的代碼中,而不在我們編寫的代碼中,這種錯誤在通常情況下是我們的程序對于參數的輸入有誤造成的,我們可以通過調用棧查看是誰調用了該函數,以便進行進一步分析。

“Register”窗口是用來觀察寄存器的。有時需要觀察返回值或者參數。

“Disassembly”窗口是用來觀察C代碼對應的反匯編代碼的。有時在看C的代碼無法解決的問題時,需要查看在底層實現時分析程序的問題。

以上就是VC6下常用的調試窗口,可根據實際情況使用,并不是每次調試都會用到這些窗口。下面再簡單介紹一下常用的調試快捷鍵,以方便今后進行調試時使用。

VC6調試時的常用快捷鍵如下。

F5鍵:運行程序。

F9鍵:設定斷點/取消斷點。

F10鍵:單步步過,依次執行每一條代碼。

F11鍵:單步步入,依次執行每一條代碼,遇到函數調用時則進入到被調用的函數中。

F7鍵:停止調試。

在后面的章節中我們會用到這些快捷鍵來調試程序,讓大家在學習的過程中真正地應用起這些調試功能。

1.2.3 專業的應用程序調試工具——OllyDbg

OllyDbg,簡稱OD,是專業的應用程序調試工具。接觸過破解,或者做過外掛開發的讀者一定對這款工具不陌生。在這里,簡單介紹一下這款工具。

讓我們先來看看它的界面吧,如圖1-12所示。

圖1-12 OllyDbg的窗口

OD的大多數情況是在沒有源代碼的情況下對軟件進行調試的。也許沒有源代碼也就不叫調試了,而叫做動態分析。OD的主界面中有6個主要的窗口,分別是反匯編窗口、寄存器窗口、提示信息窗口、數據窗口(也叫轉存窗口)、棧窗口和命令提示窗口。

下面逐個介紹一下各個窗口的作用。

(1)反匯編窗口:這是調試或動態分析時的主要窗口,我們主要是針對軟件的功能實現進行分析,因此主要需查看的就是反匯編窗口的內容。

(2)寄存器窗口:該窗口的作用是實時地顯示寄存器的變化情況。寄存器也可以反映代碼的執行情況。例如,我們常常查看返回值的eax的值。

(3)提示信息窗口:這里往往會顯示一些內存地址的值、寄存器的值、調用方的地址等信息。

(4)數據窗口:該窗口主要是用來顯示數據的,單擊右鍵可以把數據按照不同的方式進行解析,對于我們分析程序的過程是非常有用的。

(5)棧窗口:該窗口可以用來查看函數調用時參數的值。

(6)命令提示窗口:該窗口是用來輸入調試命令的。

OD調試時的常用快捷鍵如下。

F8鍵:單步步過,依次執行每一條代碼。

F7鍵:單步步入,依次執行每一條代碼,遇到函數調用時則進入到被調用的函數中。

F4鍵:執行到選中的代碼處(前提條件是該條代碼在程序的流程中一定會被執行到)。

F2鍵:斷點中斷。

F9鍵:運行程序。

OD的介紹到此為止,在后面的內容中我們會再次提到OD,到那時會有一定的機會練習使用OD。如果有對OD感興趣的讀者,請另行閱讀其他書籍。

1.3 簡單API的介紹

下面介紹一些在黑客編程中會用到的API函數,盡量排一點簡單易用的函數,用簡單的幾行代碼來完成一定的功能,希望大家能在這里體會到編程樂趣,不至于被大段的代碼影響了自己前進的心情。

1.3.1 復制自身程序到Windows目錄和系統目錄下

一般的病毒木馬都有這種類似的功能,完成這個功能其實并不復雜,我們來拆解思考一下實現這段代碼的步驟。

復制是一個拷貝的過程。既然是拷貝,就要知道拷貝的原位置和目的位置。也就是整個過程其實分3步,首先要得到自身程序所在的路徑,然后獲得Windows目錄和系統目錄,最后分別拷貝自身程序到這兩個目錄中。這3個步驟要如何完成,下面我們來看看完成這些功能的API函數。

獲得自身程序所在路徑的API函數的定義:

DWORD GetModuleFileName(
 HMODULE hModule,   // handle to module
 LPTSTR lpFilename, // file name of module
 DWORD nSize       // size of buffer
);

該函數有3個參數,分別如下。

(1)hModule:該參數在獲得自身程序時使用為NULL。

(2)lpFilename:該參數指定一個字符型的緩沖區,用于保存程序自身所在的路徑。

(3)nSize:該參數指定緩沖區的大小。

獲得Windows目錄的API函數的定義:

UINT GetWindowsDirectory(
 LPTSTR lpBuffer, // buffer for Windows directory
 UINT uSize     // size of directory buffer
);

該函數有兩個參數,分別如下。

(1)lpBuffer:該參數指定一個字符型的緩沖區,用于保存Windows目錄的路徑。

(2)uSize:該參數指定緩沖區的大小。

獲得系統目錄的API函數的定義:

UINT GetSystemDirectory(
 LPTSTR lpBuffer, // buffer for system directory
 UINT uSize     // size of directory buffer
);

該函數有兩個參數,分別如下。

(1)lpBuffer:該參數指定一個字符型的緩沖區,用于保存系統目錄的路徑。

(2)uSize:該參數指定緩沖區的大小。

拷貝文件的API函數的定義:

BOOL CopyFile(
 LPCTSTR lpExistingFileName, // name of an existing file
 LPCTSTR lpNewFileName,     // name of new file
 BOOL bFailIfExists        // operation if file exists
);

該函數有3個參數,分別如下。

(1)lpExistingFileName:該參數指向一個已存在文件的路徑,即原文件路徑。

(2)lpNewFileName:該參數指向一個新的文件的位置,即欲拷貝到的文件的目的路徑。

(3)bFailIfExists:該參數是一個布爾型參數,如果參數為TRUE,若目的文件已存在則返回,復制失敗;如果參數為FALSE,若目的文件已存在則強行覆蓋原有的文件。

需要使用的API函數已經介紹完了,下面就來真正完成這個復制自身程序到Windows目錄和系統目錄下的程序,代碼如下:

void CopySelf()
{
 // 保存自身程序的路徑
 char szSelfName[MAX_PATH] = { 0 };
 // 保存Windows目錄的路徑
 char szWindowsPath[MAX_PATH] = { 0 };
 // 保存系統目錄的路徑
 char szSystemPath[MAX_PATH] = { 0 };
 // 臨時路徑變量
 char szTmpPath[MAX_PATH] = { 0 };
 GetModuleFileName(NULL, szSelfName, MAX_PATH);
 GetWindowsDirectory(szWindowsPath, MAX_PATH);
 GetSystemDirectory(szSystemPath, MAX_PATH);
 strcat(szWindowsPath, "\\backdoor.exe");
 strcat(szSystemPath, "\\backdoor.exe");
 CopyFile(szSelfName, szWindowsPath, FALSE);
 CopyFile(szSelfName, szSystemPath, FALSE);
}

該函數需要包含Windows.h這個頭文件,也就是在該段程序的最開始處加一句:

#include <windows.h>

1.3.2 獲得系統的相關信息

了解一個系統相關信息也是一項比較重要的內容,強大的掃描軟件Nmap在對目標主機進行掃描時,也能對目標主機的系統等信息進行識別,真的是很強大。這里簡單地獲取一些與系統相關的信息,主要獲取的內容有操作系統的版本、操作系統的名字及當前登錄的用戶名稱。接下來逐個介紹這些API函數。

(1)獲取操作系統版本

代碼如下:

BOOL GetVersionEx(
 LPOSVERSIONINFO lpVersionInfo // version information
);

該函數就一個參數,這個參數是指向一個OSVERSIONINFO結構的指針。看一下OSVERSIONINFO這個結構體。

typedef struct _OSVERSIONINFO{
 DWORD dwOSVersionInfoSize; // 結構體大小
 DWORD dwMajorVersion;     // 主版本號
 DWORD dwMinorVersion;     // 次版本號
 DWORD dwBuildNumber;
 DWORD dwPlatformId;      // 平臺ID
 TCHAR szCSDVersion[ 128 ]; // 補丁包
} OSVERSIONINFO;

dwPlatformId的取值有3個,而現在主要使用一個,即VER_PLATFORM_WIN32_NT。

(2)獲取計算機名稱

代碼如下:

BOOL GetComputerName(
 LPTSTR lpBuffer, // computer name
 LPDWORD lpnSize // size of name buffer
);

該函數有兩個參數,介紹如下。

① lpBuffer:保存計算機名稱緩沖區。

② lpnSize:保存緩沖區的長度,該參數是一個輸入/輸出參數。

(3)獲取當前用戶名稱

代碼如下:

BOOL GetUserName(
 LPTSTR lpBuffer, // name buffer
 LPDWORD nSize    // size of name buffer
);

該函數有兩個參數,介紹如下。

① lpBuffer:保存當前用戶名稱的緩沖區。

② nSize:保存緩沖區的長度,該參數是一個輸入/輸出參數。

我們封裝一個簡單的函數來獲取系統的這3個信息,代碼如下:

void GetSysInfo()
{
 char szComputerName[MAXBYTE] = { 0 };
 char szUserName[MAXBYTE] = { 0 };
 unsigned long nSize = MAXBYTE;
 OSVERSIONINFO OsVer;
 OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 GetVersionEx(&OsVer);
 if ( OsVer.dwPlatformId == VER_PLATFORM_WIN32_NT )
 {
  if ( OsVer.dwMajorVersion == 5 && OsVer.dwMinorVersion == 1 )
  {
   printf("Windows XP %s \r\n", OsVer.szCSDVersion);
  }
  else if ( OsVer.dwMajorVersion == 5 && OsVer.dwMinorVersion == 0)
  {
   printf("Windows 2K \r\n");
  }
 }
 else
 {
  printf("Ohter System \r\n");
 }
 GetComputerName(szComputerName, &nSize);
 printf("Computer Name is %s \r\n", szComputerName);
 nSize = MAXBYTE;
 GetUserName(szUserName, &nSize);
 printf("User Name is %s \r\n", szUserName);
}

將代碼進行編譯連接并運行,其執行結果如圖1-13所示。

圖1-13 當前操作系統版本、計算機名及當前用戶名

這個程序完成了我們想要的功能,對于編程的部分就介紹到這里。下面介紹Debug和Release方面的內容。

1.3.3 Debug和Release的編譯方式

關于獲取系統信息的程序,我們編寫完成了,也編譯連接并運行過了。找到剛才編譯的程序,查看一下它的文件大小,如圖1-14所示。

圖1-14 GetSysInfo的程序大小

從圖1-14中可以看出,該程序竟然有153KB大小。是不是很驚人?我們一共寫了不過十幾行代碼,但是卻生成了如此大體積的程序,這是為什么呢?因為代碼默認編譯連接是Debug版本的,如圖1-15所示。

圖1-15 Win32 Debug方式

從圖1-15中可以看出,我們的代碼是由Debug方式編譯的。Debug被稱為調試版本,在這種方式的編譯下,可執行程序中會附帶很多和調試相關的數據或代碼,而且不做任何的優化,以此為開發人員提供大量的調試信息,從而方便了程序的調試工作。除了Debug方式編譯以外,還有一種方式是Release方式編譯,單擊“Win32 Debug”右邊的下拉箭頭可以選擇“Win32 Release”,如圖1-16所示。

圖1-16 Win32 Release方式

Release方式被稱作發布版本,是為最終用戶使用的,這種方式對代碼做了大量的優化工作,不再包含與調試相關的信息,從而使程序的運行效率更高,體積更小,如圖1-17所示。

圖1-17 Release版的GetSysInfo的文件大小

從圖1-17可以看出,兩個程序的文件大小發生了截然不同的變化。因此,當我們自己寫程序調試時,應該使用調試版,以方便我們對程序進行調試。當我們的程序已經調試完畢,那么可以使用發布版來與大家進行交流。

1.3.4 查看函數定義

很多時候,我們都需要查看函數的定義,而函數的定義都在SDK的頭文件中。雖然從MSDN中也能找到函數的定義,但是還是有略微的不同,而且對于查找自定義函數的函數定義也是很方便的。

回到我們的代碼當中,隨便選中一個API函數,比如GetComputerName()這個函數。加入要查看該函數的定義應該如何查看呢?我們在GetComputerName()這個函數上單擊鼠標右鍵,在彈出的快捷菜單上選擇“Go To Definition Of GetComputerName”(到GetComputerName函數的定義處)命令,如圖1-18所示。

圖1-18 “Go To Definition Of GetComputerName”命令

當選擇“Go To Definition Of GetComputerName”命令以后,會來到“Winbase.h”頭文件中的GetComputerName()函數的定義處,如圖1-19和圖1-20所示。

圖1-19 “Winbase.h”頭文件

圖1-20 GetComputerName()的定義

從圖1-20中可以看出,GetComputerName是一個宏,其對應的函數為GetComputerNameA()。關于GetComputerName()和GetComputerNameA(),包括可以看到的GetComputerNameW(),我們都不進行介紹。通過圖1-20的函數定義和前面介紹這個函數的定義來比較一下,可以看到,頭文件中的定義比MSDN中的定義對于函數的描述更加詳細,比如WINAPI表示函數的調用方式。

除了“Go To Definition Of GetComputerName”以外,還有一個“Go To Reference ToGetComputerName”,這個是查看何處引用了函數。大家可以自行進行練習。

1.4 總結

本書的編程內容主要以C語言為編程語言(本書部分內容會涉及其他語言,但C語言是主要的),以VC6為開發環境,著重介紹了VC6的基本概念及簡單的使用,在此基礎上帶領大家認識了專業的應用程序的調試工具——OD。在最后的內容中介紹了一些簡單的API函數的使用。萬丈高樓平地起,希望每一位初學者不要過于著急,在后面的章節中我們會慢慢地深入學習黑客編程的內容。希望在每學完一個知識后,大家多思考多動手,這樣才能真正地起到學習的效果。

主站蜘蛛池模板: 江山市| 岱山县| 伊春市| 广宁县| 凌源市| 昌图县| 密云县| 大理市| 巴塘县| 金阳县| 桐城市| 南汇区| 尼玛县| 鹰潭市| 邢台市| 得荣县| 塘沽区| 台前县| 夏邑县| 阿克| 广东省| 武清区| 南汇区| 邮箱| 社旗县| 兴安县| 潼关县| 朔州市| 托克托县| 丰台区| 林芝县| 花莲县| 尚义县| 澳门| 五莲县| 吉安县| 潜江市| 贞丰县| 宁强县| 塔城市| 甘泉县|