(https://ethernaut.openzeppelin.com/level/0x11343d543778213221516D004ED82C45C3c8788B)
這一關有一個函數叫做unlock,會把locked這個變數變成false,只要能成功呼叫unlock來使locked變數變為false,即可過關。困難點在於: 需要知道_key的值,才能達成。
bool public locked = true;
uint256 public ID = block.timestamp;
uint8 private flattening = 10;
uint8 private denomination = 255;
uint16 private awkwardness = uint16(now);
bytes32[3] private data;
function unlock(bytes16 _key) public {
require(_key == bytes16(data[2]));
locked = false;
}
回想一下,這一關跟level 8 : Valut 很像,因為_key == bytes16(data[2]),只要我們算出data[2]的儲存position,就可以呼叫web3.eth.getStorageAt(instance, position)來取得data[2]的值;參考下圖來計算position,bool locked佔position 0,ID佔position 1,flattening+denomination+awkwardness 佔 1個slot(postion 2),來到data,data[0]佔position 3,data[1]佔postion 4,所以 data[2]佔 position …..
var rawKey = await web3.eth.getStorageAt(instance, 5);
var asKey = '0x' + rawKey.slice(2,34); //變數的長相開頭為0x + 後續16bytes
await contract.unlock(asKey)
另外,程式將data[2] (為一個32 bytes的變數) cast成 16 bytes,因此我們取得getStorageAt(5)的值後,需要根據變數的長相取其前16 bytes,有了上述的理解後,打開console,輸入以下,即可過關囉
這關就解題而言其實主要了解變數存儲的結構及利用結構取得變數值即可過關,但這一題提醒我們關於變數宣告時,應該考慮到儲存的效率性,(可參考上圖中,盡量將nil的部分補滿)。例如有兩個uint128及一個uint256的變數時,應該先宣告那兩個uint 128,再宣告uint 256的變數,這樣的話僅會佔2個儲存格,若先宣告一個uint128,再宣告unit256,最後又宣告uint 128,則會變成需要3個儲存格,在區塊鍊的世界裡,儲存格的使用量是跟成本直接掛鉤的! 對於使用習慣高階程式語言的程式設計師,實在沒有特別注意memory安排的習慣呢。
1 thought on “Ethernaut – level 12 : Privacy”