中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

如何從零開始寫一個加殼器

本篇內(nèi)容介紹了“如何從零開始寫一個加殼器”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:主機域名、網(wǎng)頁空間、營銷軟件、網(wǎng)站建設(shè)、永定網(wǎng)站維護、網(wǎng)站推廣。

加殼原理

手工加殼

即我們向PE文件添加一個區(qū)段并將其設(shè)置為入口點,這樣PE文件最開始執(zhí)行的命令就是我們添加的區(qū)段也就是殼的指令,殼對加密區(qū)進行解密,對壓縮區(qū)進行解壓,將原本的EXE文件還原出來,然后跳轉(zhuǎn)至原程序入口,程序照常運行。

首先生成一個打印hello的exe文件。

#include <stdio.h>

int main() {
printf("hello");
}

我們目前要干的事情是:以手動的形式向PE文件添加一個殼部分并設(shè)為程序入口,并使其能跳轉(zhuǎn)回原入口。 那就來吧

用010editor打開我們的exe文件,啟用exe模板分析。 我們首先修改其文件頭numverofsection屬性,這個屬性用來定義當前PE文件存在多少個區(qū)段,因為我們要添加一個殼區(qū)段,所以我們將其加1變成6

如何從零開始寫一個加殼器

在我們重載模板后我們就會在區(qū)段表發(fā)現(xiàn)多出來一個空的區(qū)段表

如何從零開始寫一個加殼器

從上到下各個比較重要字段的意思是 \1. Name 表示該區(qū)段的名字 2.VirtualSize 表示在內(nèi)存中的大小(一般內(nèi)存對齊為0x1000) 3.virtualaddress 虛擬地址 即上一個區(qū)段的VirtualAddress + 上一個區(qū)段經(jīng)內(nèi)存對齊粒度對齊后的大小 4.sizeofdata 表示在文件中的大?。ㄒ话阄募R為0x200) 5.pointertorawdata 文件的偏移 即 上一個區(qū)段的PointerToRawData + 上一個區(qū)段的SizeOfRawData

然后我們通過修改以上各值來定義一個新區(qū)段(殼區(qū)段)的屬性

如何從零開始寫一個加殼器

這里的virtualsize看著填一個就行了。 此時我們只是定義了區(qū)段表,但文件中并沒有該區(qū)段存在,所以我們得創(chuàng)建該區(qū)段。 然后還要讓區(qū)段可編輯,把下列值改為1即可

如何從零開始寫一個加殼器

如何從零開始寫一個加殼器

ctrl+shift+i 向目標文件偏移處插入0x200大小的空間。 這樣一來,殼區(qū)段就創(chuàng)建好了。 然后我們還要修改 擴展頭的SizeofImage 。將他改為最后一個區(qū)段的內(nèi)存地址+內(nèi)存大小

如何從零開始寫一個加殼器

然后去掉隨機基址選項。

找到擴展頭的DLL屬性字段,去掉隨機基址,把40 81改為 00 81

如何從零開始寫一個加殼器

接下來我們把程序入口點設(shè)置給殼區(qū)段。 使用LORDPE把入口點設(shè)為殼區(qū)塊的虛擬地址

然后我們用OD打開這個文件

如何從零開始寫一個加殼器

如何從零開始寫一個加殼器

真正的加殼流程

剛剛提到的手工加殼,不過是最最基礎(chǔ)的加殼原型而已,真正的加殼還涉及了代碼加解密等操作.

真正寫殼時一般寫兩個東西,加殼器和stub 所謂加殼器,就是給被加殼文件創(chuàng)造出一個新的區(qū)段, 在此同時將程序以某種方式加密,然后把stub放入新區(qū)段,并將程序入口點設(shè)為新區(qū)段的地址,然后在新區(qū)段結(jié)束后跳轉(zhuǎn)回原程序入口。這個新區(qū)段我們叫做殼區(qū)段. 那么這個stub就是加殼后程序最先執(zhí)行的命令了,它執(zhí)行解密算法,將原程序釋放出來。

基于c++的殼編寫

https://github.com/ConsT27/PackingEXE/tree/master項目地址

Stub

stub是被植入到PE文件中的代碼,它一般會干下面這些事情。

流程如下

0.合并data,rdata到text 1.PEB動態(tài)尋址,遍歷導(dǎo)出表找到GetProcAddress函數(shù) 2.解密 3.修改入口點到原入口點

同時stub一般以dll的形式存在。原因是DLL通常自帶重定位表,這在我們的移植過程中的重定位操作中提供了巨大的便利。

合并數(shù)據(jù)段

我們要移植stub過去,肯定需要移植代碼段,也需要移植數(shù)據(jù)段。不如我們干脆把數(shù)據(jù)段合并到代碼段,一塊移植過去。

PEB動態(tài)尋址&導(dǎo)出表遍歷找函數(shù)

為什么會用到這個技術(shù)編寫stub? 因為我們的stub.dll植入到宿主程序時,只有.text植入過去,沒有對應(yīng)的導(dǎo)入表,所以我們的stub無法直接調(diào)用一些API。所以我們需要動態(tài)獲取各種API。 其中我采用的是PEB動態(tài)查詢得到GetProcAddress函數(shù),然后用GetProcAddress函數(shù)去獲取各個API。

那么,什么是PEB? PEB是一個微軟還未完全公開作用的一個結(jié)構(gòu),它叫做 進程環(huán)境信息塊 ,包含了進程的信息。其結(jié)構(gòu)如下

typedef struct _PEB {
BYTE                          Reserved1[2];
BYTE                          BeingDebugged; //被調(diào)試狀態(tài)
BYTE                          Reserved2[1];
PVOID                         Reserved3[2];
PPEB_LDR_DATA                 Ldr;
PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
BYTE                          Reserved4[104];
PVOID                         Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE                          Reserved6[128];
PVOID                         Reserved7[1];
ULONG                         SessionId;
} PEB, *PPEB;
復(fù)制代碼

我們關(guān)心的是PEB偏移0c得到的 PPEB_LDR_DATA Ldr; 它是一個指針,指向一個 PPEB_LDR_DATA 結(jié)構(gòu), 存放著已經(jīng)被進程裝在的動態(tài)鏈接庫的信息

typedef struct _PEB_LDR_DATA
{
ULONG Length; // +0x00
BOOLEAN Initialized; // +0x04
PVOID SsHandle; // +0x08
LIST_ENTRY InLoadOrderModuleList; // +0x0c
LIST_ENTRY InMemoryOrderModuleList; // +0x14
LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24

PPEB_LDR_DATA 偏移1c是一個指向LIST_ENTRY InInitializationOrderModuleList結(jié)構(gòu)的指針,這個結(jié)構(gòu) 存放著指向模塊初始化鏈表的頭 , 按順序存放著PE裝入運行時初始化模塊信息,一般來說第一個鏈表結(jié)點是ntdll.dll,第二個鏈表結(jié)點就是kernel32.dll 。我們就在其中找到kernel32.dll的信息,獲取其PE信息,得到導(dǎo)出表,循環(huán)遍歷得到GetProcAddress函數(shù)。 另外,PEB地址再TEB偏移0x30處。用匯編語言表示就是 fs:[0x30]。

以上是PEB尋址的大致流程,另外還有一個比較關(guān)鍵的點是遍歷kernel32.dll導(dǎo)出表獲得GetProcAddress函數(shù)信息。 關(guān)于導(dǎo)出表可以看看這個文章https://blog.csdn.net/evileagle/article/details/12176797

首先一個導(dǎo)出表結(jié)構(gòu)體如下

typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD   Characteristics;  //一般為0,沒啥用
DWORD   TimeDateStamp;  //導(dǎo)出表生成的時間
WORD    MajorVersion;  //版本,也是0沒啥用
WORD    MinorVersion;  //也是沒啥用的版本信息一般為0
DWORD   Name;  //當前導(dǎo)出表的模塊名字
DWORD   Base;  //序號表中序號的基數(shù)
DWORD   NumberOfFunctions;  //導(dǎo)出函數(shù)數(shù)量
DWORD   NumberOfNames;  //按名字導(dǎo)出函數(shù)的數(shù)量
DWORD   AddressOfFunctions;     // 序號表
DWORD   AddressOfNames;         // 名稱表
DWORD   AddressOfNameOrdinals;  // 地址表
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

其中序號表的起始序號是Base屬性定義的值。以下是導(dǎo)出表的序號名稱地址表的關(guān)系

如何從零開始寫一個加殼器

我們的遍歷流程是,先遍歷名稱表找到GetProcAddress在名稱數(shù)組中的下標,然后根據(jù)這個下標去序號數(shù)組中找相同下標的序號值,然后以這個序號值為下標去找地址數(shù)組中的對應(yīng)值。我們找到的地址表中的值就是函數(shù)入口

下面我把這段程序的匯編代碼放出來。我是用內(nèi)聯(lián)匯編把這段代碼塞進C++的

void GetApis()
{
HMODULE hKernel32;

_asm
{
pushad;
; //獲取kernel32.dll的加載基址;
mov eax, fs: [0x30] ;  //得到PEB地址
mov eax, [eax + 0ch];  //獲得LDR_PEB_DATA地址
mov eax, [eax + 0ch];  //獲得LIST_ENTRY InLoadOrderModuleList;地址
mov eax, [eax];  //獲得LIST_ENTRY InLoadOrderModuleList下一項的地址
mov eax, [eax];  /獲得LIST_ENTRY InLoadOrderModuleList下下項即我們需要的LIST_ENTRY InInitializationOrderModuleList的地址
mov eax, [eax + 018h]; //獲得kernel32.dll地址
mov hKernel32, eax;
mov ebx, [eax + 03ch];//獲得kernel32.dll NT頭RVA
add ebx, eax; //NT頭的VA
add ebx, 078h; //獲得區(qū)段表
mov ebx, [ebx]; //獲得導(dǎo)出表RVA
add ebx, eax;  //導(dǎo)出表VA
lea ecx, [ebx + 020h];  
mov ecx, [ecx]; // ecx => 名稱表的首地址(rva);
add ecx, eax; // ecx => 名稱表的首地址(va);
xor edx, edx; // 作為索引(index)來使用.
_WHILE:;
mov esi, [ecx + edx * 4];//名稱數(shù)組入口點rva,名稱數(shù)組單位大小4字節(jié)
lea esi, [esi + eax];  //入口點VA
cmp dword ptr[esi], 050746547h;   //進行名稱匹配,050746547h即小端存儲的GetP
jne _LOOP;//不相等就跳入_LOOP段
cmp dword ptr[esi + 4], 041636f72h; //名陳匹配,rocA,以下依次為ddre,ss
jne _LOOP;
cmp dword ptr[esi + 8], 065726464h;
jne _LOOP;
cmp word  ptr[esi + 0ch], 07373h;
jne _LOOP;
mov edi, [ebx + 024h]; 
add edi, eax;  //獲得序號表VA

mov di, [edi + edx * 2];  //獲得序號數(shù)組中對應(yīng)下標的地址,序號數(shù)組單位大小2字節(jié)
and edi, 0FFFFh;  //給di提位到32位,即給予edi 序號表中對應(yīng)下標的地址
mov edx, [ebx + 01ch];  
add edx, eax;  //獲得地址表
mov edi, [edx + edi * 4];  //獲得地址數(shù)組中,序號對應(yīng)的值,地址數(shù)組單位大小4字節(jié)
add edi, eax;   //獲得GetProcAddress的入口地址
mov MyGetProcAddress, edi;  //賦值
jmp _ENDWHILE;  //END
_LOOP:;
inc edx; // ++index;
jmp _WHILE;
_ENDWHILE:;
popad;
}
解密

解密代碼段。這段好寫。

void Decrypt()
{
unsigned char* pText = (unsigned char*)g_conf.textScnRVA + 0x400000;//鎖定到PE文件的text段(因為加殼時去掉了基址隨機化,所以自信的把基址填成0x400000

DWORD old = 0;
MyVirtualProtect(pText, g_conf.textScnSize, PAGE_READWRITE, &old);//修改代碼段的屬性,注意我們這里使用了動態(tài)獲得的
//解密代碼段
for (DWORD i = 0; i < g_conf.textScnSize; i++)
{
pText[i] ^= g_conf.key;
}
//把屬性修改回去
MyVirtualProtect(pText, g_conf.textScnSize, old, &old);

}
修改入口點
_asm    
{
mov eax, g_conf.srcOep;  //入口點是g_conf.srcOep
add eax, 0x400000
jmp eax
}

加殼器

加殼器流程如下

1.打開需要被加殼的PE文件 2.加載stub 3.加密代碼段 4.添加新區(qū)段 5.stub重定位修復(fù) 6.stub移植 7.PE文件入口點修改 8.去隨機基址 9.保存文件

以下的各個流程描述中會用到諸多自定義函數(shù),我先貼上來吧。

諸多自定函數(shù)&結(jié)構(gòu)體
//****************
//對齊處理
//time:2020/11/5
//****************
int AlignMent(_In_ int size, _In_ int alignment) {
return (size) % (alignment)==0 ? (size) : ((size) / alignment+1) * (alignment);
}

//***********************
//PE信息獲取函數(shù)簇
//time:2020/11/2
//***********************
PIMAGE_DOS_HEADER GetDosHeader(_In_ char* pBase) {
return PIMAGE_DOS_HEADER(pBase);
}

PIMAGE_NT_HEADERS GetNtHeader(_In_ char* pBase) {
return PIMAGE_NT_HEADERS(GetDosHeader(pBase)->e_lfanew+(SIZE_T)pBase);
}

PIMAGE_FILE_HEADER GetFileHeader(_In_ char* pBase) {
return &(GetNtHeader(pBase)->FileHeader);
}

PIMAGE_OPTIONAL_HEADER32 GetOptHeader(_In_ char* pBase) {
return &(GetNtHeader(pBase)->OptionalHeader);
}

PIMAGE_SECTION_HEADER GetLastSec(_In_ char* pBase) {
DWORD SecNum = GetFileHeader(pBase)->NumberOfSections;
PIMAGE_SECTION_HEADER FirstSec = IMAGE_FIRST_SECTION(GetNtHeader(pBase));
PIMAGE_SECTION_HEADER LastSec = FirstSec + SecNum - 1;
return LastSec;
}

PIMAGE_SECTION_HEADER GetSecByName(_In_ char* pBase,_In_ const char* name) {
DWORD Secnum = GetFileHeader(pBase)->NumberOfSections;
PIMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION(GetNtHeader(pBase));
char buf[10] = { 0 };
for (DWORD i = 0; i < Secnum; i++) {
memcpy_s(buf, 8, (char*)Section[i].Name, 8);
if (!strcmp(buf, name)) {
return Section + i;
}
}
return nullptr;
}

typedef struct _StubConf
{
DWORD srcOep;       //入口點
DWORD textScnRVA;   //代碼段RVA
DWORD textScnSize;  //代碼段的大小
DWORD key;          //解密密鑰
}StubConf;

struct StubInfo
{
char* dllbase;          //stub.dll的加載基址
DWORD pfnStart;         //stub.dll(start)導(dǎo)出函數(shù)的地址
StubConf* pStubConf;    //stub.dll(g_conf)導(dǎo)出全局變量的地址
};

打開PE文件

這里采用的方法是利用CreateFileA函數(shù)。同時這個函數(shù)還拋出了一個指向PE文件大小的指針

char* GetFileHmoudle(_In_ const char* path,_Out_opt_ DWORD* nFileSize) {
//打開一個文件并獲得文件句柄
HANDLE hFile = CreateFileA(path,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
//獲得文件大小
DWORD FileSize = GetFileSize(hFile, NULL);
//返回文件大小到變量nFileSize
if(nFileSize)
*nFileSize = FileSize;
//申請一片大小為FileSize的內(nèi)存并將指針置于首位
char* pFileBuf = new CHAR[FileSize]{ 0 };
//給剛剛申請的內(nèi)存讀入數(shù)據(jù)
DWORD dwRead;
ReadFile(hFile, pFileBuf, FileSize, &dwRead, NULL);
CloseHandle(hFile);
return pFileBuf;
}

加載STUB

void LoadStub(_In_ StubInfo* pstub) {
pstub->dllbase = (char*)LoadLibraryEx(L"F:\\stubdll.dll", NULL, DONT_RESOLVE_DLL_REFERENCES);
pstub->pfnStart = (DWORD)GetProcAddress((HMODULE)pstub->dllbase, "Start");  //獲得stub的入口函數(shù)Start(自己定義在stub中的一個函數(shù)
pstub->pStubConf = (StubConf*)GetProcAddress((HMODULE)pstub->dllbase, "g_conf");
}
//不僅加載了stub,還獲得了stub拋出的用于收集信息的全局結(jié)構(gòu)體(g_conf,是一個stub拋出的結(jié)構(gòu)體,用于獲取信息,結(jié)構(gòu)如下)
typedef struct _StubConf
{
DWORD srcOep;       //入口點
DWORD textScnRVA;   //代碼段RVA
DWORD textScnSize;  //代碼段的大小
DWORD key;          //解密密鑰
}StubConf;

加密代碼段

DWORD textRVA = GetSecByName(PeHmoudle, ".text")->VirtualAddress;
DWORD textSize = GetSecByName(PeHmoudle, ".text")->Misc.VirtualSize;
Encry(PeHmoudle,pstub);
void Encry(_In_ char* hpe,_In_ StubInfo pstub) {
//獲取代碼段首地址
BYTE* TargetText = GetSecByName(hpe, ".text")->PointerToRawData + (BYTE*)hpe;
//獲取代碼段大小
DWORD TargetTextSize = GetSecByName(hpe, ".text")->Misc.VirtualSize;
//加密代碼段
for (int i = 0; i < TargetTextSize; i++) {
TargetText[i] ^= 0x15;
}
pstub.pStubConf->textScnRVA = GetSecByName(hpe, ".text")->VirtualAddress;
pstub.pStubConf->textScnSize = TargetTextSize;
pstub.pStubConf->key = 0x15;
}
//加密代碼段,并給予了stub一些信息
添加新區(qū)段
char* AddSec(_In_ char*& hpe, _In_ DWORD& filesize, _In_ const char* secname, _In_ const int secsize) {
GetFileHeader(hpe)->NumberOfSections++;
PIMAGE_SECTION_HEADER pesec = GetLastSec(hpe);
//設(shè)置區(qū)段表屬性
memcpy(pesec->Name, secname, 8);
pesec->Misc.VirtualSize = secsize;
pesec->VirtualAddress = (pesec - 1)->VirtualAddress + AlignMent((pesec - 1)->SizeOfRawData,GetOptHeader(hpe)->SectionAlignment);
pesec->SizeOfRawData = AlignMent(secsize, GetOptHeader(hpe)->FileAlignment);
pesec->PointerToRawData = AlignMent(filesize,GetOptHeader(hpe)->FileAlignment);
pesec->Characteristics = 0xE00000E0;
//設(shè)置OPT頭映像大小
GetOptHeader(hpe)->SizeOfImage = pesec->VirtualAddress + pesec->SizeOfRawData;
//擴充文件數(shù)據(jù)
int newSize = pesec->PointerToRawData + pesec->SizeOfRawData;
char* nhpe = new char [newSize] {0};
//向新緩沖區(qū)錄入數(shù)據(jù)
memcpy(nhpe, hpe, filesize);
//緩存區(qū)更替
delete hpe;
filesize = newSize;
return nhpe;
}

stub重定位

好家伙,這個東西稍有不慎就會讓整個程序拉跨掉(過來人的忠告 為什么需要stub重定位呢?因為我們的stub最開始是加載在內(nèi)存中的,它的許多指令如跳轉(zhuǎn)到的地址是按內(nèi)存為基準確定的,但是我們需要把他移植進文件,所以它的代碼里許多地址就是錯誤的,我們需要對這些地址進行處理,即重定位,使其以宿主程序為標準進行地址修復(fù)。 可能我表述的不是很清楚

舉個例子吧,比如stub在加載進內(nèi)存時,有一條跳轉(zhuǎn)指令時jmp 12345678, 如果我們不處理就把這條指令移植進PE文件,那么PE文件執(zhí)行到此處時就會跳轉(zhuǎn)到12345678,此時的12345678地址可能就已經(jīng)不是PE文件加載的內(nèi)存區(qū)間了,從而程序會崩潰。所以要修復(fù)。根據(jù)stub的重定位表進行修復(fù)。 重定位表就是記錄哪些地址的數(shù)據(jù)需要被修復(fù)的,我們遍歷這些地址進行修復(fù)即可。 如果以下代碼看起來吃力,可以先去了解一下重定位表

void FixStub(DWORD targetDllbase, DWORD stubDllbase,DWORD targetNewScnRva,DWORD stubTextRva )
{
//找到stub.dll的重定位表
DWORD dwRelRva = GetOptHeader((char*)stubDllbase)->DataDirectory[5].VirtualAddress;
IMAGE_BASE_RELOCATION* pRel = (IMAGE_BASE_RELOCATION*)(dwRelRva + stubDllbase);

//遍歷重定位表
while (pRel->SizeOfBlock)
{
struct TypeOffset
{
WORD offset : 12;
WORD type : 4;

};
TypeOffset* pTypeOffset = (TypeOffset*)(pRel + 1);
DWORD dwCount = (pRel->SizeOfBlock - 8) / 2;    //需要重定位的數(shù)量
for (int i = 0; i < dwCount; i++)
{
if (pTypeOffset[i].type != 3)
{
continue;
}
//需要重定位的地址
DWORD* pFixAddr = (DWORD*)(pRel->VirtualAddress + pTypeOffset[i].offset + stubDllbase);

DWORD dwOld;
//修改屬性為可寫
VirtualProtect(pFixAddr, 4, PAGE_READWRITE, &dwOld);
//去掉dll當前加載基址
*pFixAddr -= stubDllbase;
//去掉默認的段首RVA
*pFixAddr -= stubTextRva;
//換上目標文件的加載基址
*pFixAddr += targetDllbase;
//加上新區(qū)段的段首RVA
*pFixAddr += targetNewScnRva;
//把屬性修改回去
VirtualProtect(pFixAddr, 4, dwOld, &dwOld);
}
//切換到下一個重定位塊
pRel = (IMAGE_BASE_RELOCATION*)((DWORD)pRel + pRel->SizeOfBlock);
}

stub移植

這個簡單,沒啥說的

memcpy(GetLastSec(PeNewHmoudle)->PointerToRawData+ PeNewHmoudle,
GetSecByName(pstub.dllbase, ".text")->VirtualAddress+pstub.dllbase,
GetSecByName(pstub.dllbase,".text")->Misc.VirtualSize);
入口點修改
GetOptHeader(PeNewHmoudle)->AddressOfEntryPoint =
pstub.pfnStart-(DWORD)pstub.dllbase-GetSecByName(pstub.dllbase,".text")->VirtualAddress+GetLastSec(PeNewHmoudle)->VirtualAddress;

去隨機基址

不去掉隨機基址,加載基址就是不固定的,不方便操作

GetOptHeader(PeNewHmoudle)->DllCharacteristics &= (~0x40);
保存文件
void SaveFile(_In_ const char* path, _In_ const char* data, _In_ int FileSize) {
HANDLE hFile = CreateFileA(
path,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
DWORD Buf = 0;
WriteFile(hFile, data, FileSize, &Buf,NULL);
CloseHandle(hFile);
}

“如何從零開始寫一個加殼器”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

新聞標題:如何從零開始寫一個加殼器
當前地址:http://www.rwnh.cn/article0/jgpsoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、微信公眾號、網(wǎng)站維護、網(wǎng)站策劃、自適應(yīng)網(wǎng)站、靜態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站
定州市| 河东区| 乡城县| 海宁市| 乌拉特中旗| 广德县| 澄江县| 黄陵县| 邮箱| 北京市| 涟源市| 曲沃县| 晋宁县| 岚皋县| 永济市| 石狮市| 乌拉特前旗| 集安市| 武定县| 金塔县| 扶沟县| 三明市| 卢湾区| 咸宁市| 连江县| 天峨县| 巴马| 天全县| 白河县| 金阳县| 三明市| 徐州市| 大安市| 东港市| 龙州县| 安泽县| 新绛县| 金华市| 张家港市| 台中县| 浪卡子县|