ethereum assembly analysis using remix DEBUGGER plugin
xxxxxxxxxx
pragma solidity =0.4.25;
contract Test {
uint256 number;
constructor(uint256 n) public {
number = n;
}
function test() public returns (string) {
return "test";
}
}
위의 테스트 코드를 사용하겠다.
remix
의 DEBUGGER
플러그인을 이용하여 동적 디버깅을 진행한다. 이 과정으로 어셈블리를 익혀보자!
코드를 컴파일한 후 컨트랙트를 Deploy
하면 transaction hash
값을 얻을 수 있다. 이를 DUBUGGER
플러그인의 인자로 넣고 디버깅을 시작하자.
그러면 위와 같은 화면을 만날 수 있다.
어셈블리가 보이는 영역은 code
영역이며 그 밑에는 step over, step into
와 같은 버튼이 존재하고 그 밑에는 메모리가 존재한다.
code
: 이더리움의 기계어가 저장되는 공간이다.
storage
: 상태 변수들이 저장되는 공간이다.
memory
: 메모리형으로 선언된 변수들이 저장되는 공간이다.(아직까지는 잘 모르겠다.)
stack
: 함수 내에 존재하는 로컬 변수들이 저장되는 공간이다.
이 외에도, call data
가 존재한다. solidity
상에서는 msg.value
에 해당하는 값이다.
위와 같이 되어있다.
스택에 값을 넣고, 이를 인자로 기계어를 실행하는 식이다. (상태 변수 값이 필요할 시 storage에서 stack으로 값을 가져와서 연산)
기계어에 대한 설명은 하나하나 설명하면 기억에도 안남으니 실습을 통해 이해해보자.
이더리움의 모든 어셈블리는 아래 링크에서 확인할 수 있다.
https://solidity.readthedocs.io/en/v0.4.24/assembly.html#opcodes
첫 번째로 나오는 어셈블리어이다.
xxxxxxxxxx
000 PUSH1 80
002 PUSH1 40
004 MSTORE
여기서 80과 40은 각각 0x80, 0x40이다.
먼저 PUSH1
명령어로 0x80, 0x40
을 스택에 집어 넣는다.
PUSHi x = stack top에 x를 ibyte 만큼 집어 넣는다. (ibyte만큼 값을 넣을 수 있는거지 패딩은 똑같이 32byte로 된다.)
이는 MSTORE(0x40, 0x80)
명령어를 뜻한다.
MSTORE = MEMORY STORE
라고 생각하면 된다.
MSTORE(a, b) = a+0x10의 메모리 위치에 b를 저장한다.
실제로 메모리에 값이 들어갔다!
근데 왜 저런식으로 0이 많이 들어갈까?
EVM
에서는 1 word = 32byte
로 정의하고 있기 때문이다!
그 다음 어셈블리어를 보자.
xxxxxxxxxx
006 DUP1
007 ISZERO
008 PUSH2 0010
011 JUMPI
먼저 DUP1
명령으로 스택에 값을 복사한다.
DUPi = 스택의 i-1번 째 행을 stack top에 집어 넣는다.
그 후, 복사한 값이 zero라면, 해당 값을 1로 바꾼다.
ISZERO(a) = a가 0이라면 값을 1로 바꾼다.
그 후, 값을 PUSH
하고 조건부 점프를 진행한다.
JUMPI(a, b) = b가 zonzero라면, 어셈블리 영역의 a로 점프한다.
여기까지 진행되면, JUMPI(0x10, 1)
이 실행될 것이므로 current pc: 016
이 될 것이다.
그 다음 어셈블리를 보자.
xxxxxxxxxx
018 PUSH1 40
020 MLOAD
021 PUSH1 20
023 DUP1
024 PUSH2 0189
027 DUP4
028 CODECOPY
0x40을 스택에 집어 넣고, MLOAD(0x40)
을 실행한다.
MLOAD(x) = 메모리의 x+0x10에 존재하는 값을 stack top에 집어 넣는다.
그 후, 0x20을 집어 넣고 이 값을 DUP1
으로 복사하며, 0189
을 집어 넣고 MLOAD
로 불러온 값을 DUP4
를 이용하여 복사한다.
그리고! CODECOPY
를 진행한다.
CODECOPY(a, b, c) = a 메모리에 call data의 b position부터 c만큼 복사한다.
즉, 우리가 함수를 실행할 때 보냈던 인자를 메모리로 복사하는 것이다.
우리의 경우는 CODECOPY(0x80, 0x189, 0x20)
이 되어 0x80
번 째 메모리에 call data + 0x189
부터 0x20
만큼 복사하게 되는 것이다.
이 뒤부터는 SWAP1
명령어가 나오는 것을 제외하고는 모두 알아봤던 명령어들이 나오므로 직접 해보길 바란다.. 재밌다^^;
SWAPi(x) = x와 stack i번 째 값을 서로 바꾼다.
직접 끝까지 해봤다면, 마지막으로 CODECOPY
를 진행하고 RETURN
을 하는 곳까지 디버깅을 진행했을 것이다.
근데 보면 그 바로 뒤에 STOP
이 있고 코드가 쭉 있을 것이다.
이건 왜일까? 우리가 디버깅한 코드는 무엇일까?
를 생각해보면서 우리가 여태까지 디버깅한 코드의 중요한 지점들을 살펴보자.
어디일까?, SSTORE
, 마지막 CODECOPY
이다.
SSTORE
은 컨트랙트 상에 정보를 영원히 저장한다.
그렇다면 마지막 CODECOPY
는 무엇일까? 이 인스트럭션을 실행하고 나면 메모리가 다음과 같이 바뀌어 있을 것이다.
이것들은 무엇일까? msg.data
에서 살펴보면 다음과 같다.
그럼 앞에 있는 코드는 무엇이고 뒤에 있는 코드는 무엇일까, 앞에 있는 코드는 constructor
를 실행시키고, 뒤에 있는 코드를 메모리에 CODECOPY
한다. 즉, deploy
하는 과정이라고 볼 수 있다.
뒤에 CODECOPY
된 코드는 실제 contract
코드이다.
정말 신기하다 ㅎㅎ
'system > material' 카테고리의 다른 글
unsortedbinbin_attck died.. (0) | 2019.11.05 |
---|---|
linux file structure attack (0) | 2019.10.28 |
modern memory safety #1 (0) | 2019.10.09 |
docker 간단한 사용법 정리 (0) | 2019.08.25 |
19회 해킹캠프 발표자료 (tcache 동작 분석 및 exploit) (0) | 2019.02.19 |