pragma solidity ^0.4.19;
contract HelloWorld {
}
-
컨트랙트
컨트랙트: 이더리움 애플리케이션은 구성 요소
모든 변수와 함수는 어느 한 컨트랙트에 속해 있음
-
Version Pragma
모든 솔라디티 소스 코드는 solidity 버전 명시로 시작
struct Person {
uint age;
string name;
}
// 2개의 원소를 담을 수 있는 고정 길이의 배열:
uint[2] fixedArray;
// 또다른 고정 배열로 5개의 스트링을 담을 수 있다:
string[5] stringArray;
// 동적 배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다:
uint[] dynamicArray;
Person[] public people;
function eatHamburgers(string _name, uint _amount) {
}
-
상태 변수 & 정수
상태 변수: 컨트랙트 저장소에 영구 저장 -> 블록체인에 기록
-
구조체, 배열, 함수 선언
위 예제만 남겼음
public은 다른 배열들이 해당 배열을 읽을 수 있음(쓰기는 X)
컨트랙트에 공개 데이터 저장 시 유용
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
function _multiply(uint a, uint b) private pure returns (uint) {
return a * b;
}
-
private
기본적으로 함수는 private 선언 -> 함수명 앞에 언더바 넣는 것이 관례
공개할 함수만 public
-
함수 제어자
함수의 상태를 변경하지 않고 보기만 하는 경우 -> view
어떤 데이터에도 접근하지 않는 경우 -> pure
-
keccak256
이더리움이 갖고 있는 SHA3의 한 버전 해시 함수
// 이벤트를 선언한다
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
// 이벤트를 실행하여 앱에게 add 함수가 실행되었음을 알린다:
IntegersAdded(_x, _y, result);
return result;
}
YourContract.IntegersAdded(function(error, result) {
// 결과와 관련된 행동을 취한다
});
- 이벤트
이벤트: 어떤 액션이 생겼을 때 이벤트 발동 -> 컨트랙트가 또 하나의 행동을 취함
// 여기에 우리가 만든 컨트랙트에 접근하는 방법을 제시한다:
var abi = /* abi generated by the compiler */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory`는 우리 컨트랙트의 public 함수와 이벤트에 접근할 수 있다.
// 일종의 이벤트 리스너가 텍스트 입력값을 취한다:
$("#ourButton").click(function(e) {
var name = $("#nameInput").val()
// 우리 컨트랙트의 `createRandomZombie`함수를 호출한다:
ZombieFactory.createRandomZombie(name)
})
// `NewZombie` 이벤트가 발생하면 사용자 인터페이스를 업데이트한다
var event = ZombieFactory.NewZombie(function(error, result) {
if (error) return
generateZombie(result.zombieId, result.name, result.dna)
})
// 좀비 DNA 값을 받아서 이미지를 업데이트한다
function generateZombie(id, name, dna) {
let dnaStr = String(dna)
// DNA 값이 16자리 수보다 작은 경우 앞 자리를 0으로 채운다
while (dnaStr.length < 16)
dnaStr = "0" + dnaStr
let zombieDetails = {
// 첫 2자리는 머리의 타입을 결정한다. 머리 타입에는 7가지가 있다. 그래서 모듈로(%) 7 연산을 하여
// 0에서 6 중 하나의 값을 얻고 여기에 1을 더해서 1에서 7까지의 숫자를 만든다.
// 이를 기초로 "head1.png"에서 "head7.png" 중 하나의 이미지를 불러온다:
headChoice: dnaStr.substring(0, 2) % 7 + 1,
// 두번째 2자리는 눈 모양을 결정한다. 눈 모양에는 11가지가 있다:
eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
// 셔츠 타입에는 6가지가 있다:
shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
// 마지막 6자리는 색깔을 결정하며, 360도(degree)까지 지원하는 CSS의 "filter: hue-rotate"를 이용하여 아래와 같이 업데이트된다:
skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
zombieName: name,
zombieDescription: "A Level 1 CryptoZombie",
}
return zombieDetails
}
- Web3.js
이더리움이 갖고 있는 자바스크립트 라이브러리
컨트랙트와 사용자의 자바스크립트 코드가 서로 상호작용
자바스크립트에서 컨트랙트의 public, event 접근 가능
https://share.cryptozombies.io/ko/lesson/1/share/
lesson 1 클리어~
파일을 실행하면 이렇게 뜬다.

아이다로 디컴파일 했을 때 main이 위와 같다. 결론부터 말하면 위 for문을 반대로 만들면 끝나는 문제이다.
//원래 코드
for ( i = 27; i >= 0; --i )
*v3++ += byte_406000[i] - i;
//수정 코드
for ( i = 0; i <= 27; ++i )
*v3++ += byte_406000[i] + i;
byte_406000의 배열은 위와 같다(16진수).
푸는 방법은 두 가지가 있다.
- 파이썬이나 C++로 코드를 작성하고 실행하기
- 어셈블리 수정
어셈블리를 이해한다면 이걸 수정하는 편이 더 편하다(코드를 작성하려면 조금 귀찮다).
간단하게 sub cl, al에서 sub를 add로만 바꿔보자.

아래 덤프를 보면 문자열이 담긴 것을 알 수 있다.

다만 경고창에 SWING{} format이 맞냐고 물어본다. 출력된 문자열을 거꾸로 하면 SWING{} format이 맞아서 직접 눈으로 풀어도 되지만 0부터 증가하며 실행되는 반복문으로 수정해보자.
일단 반복문 시작 전 eax에 초기화를 해주고 있는데 이부분을 1B가 아닌 0으로 설정해주고 sub cl, al은 아까와 같이 add cl, al로 바꿔준다. sub eax, 1을 add eax, 1로 바꿔준다.
그리고 f8을 누르며 동적 분석해준다.

덤프를 보면 맞게 쓰였다. 이걸 출력까지 해주려면 }까지 루프를 돌려주고 멈춰야 한다. 임의로 코드를 수정했기 때문에 jns가 무한 루프문이 된다. 여길 나가려면 FLAG에 SF를 0에서 1로 바꿔준다.

오른쪽 참조.

이러고 f8을 또 열심히 누르면 디버깅창에 flag가 알맞게 뜬다.
-
31기 함은지 님 “SWING_JJANG” is not plaintext
Correct 문자열이 존재한다.

hi 한 번 입력하고 동적분석 진행하면

NRDIB_EEVIB라는 문자열이 보인다

다시 실행하여 입력해주면

성공
-
31기 지현아 님
문자열을 찾아준다

아무거나 입력해보고

배열 하나씩 검사하는 거 같다


이렇다 할 문자열이 안 보였다

아이다로 디컴파일 했더니

v8+=v6^0x41 주목
이 v8이 25가 되면 성공하는 문제이다.

0x44^0x41이 0x05이므로 대문자 D를 5번 입력해주면 총 25가 된다.

성공
-
31기 노희민 님 알맞은 코드를 입력하시오.
PNG 파일

푸터 시그니처가 없는 걸 보아 그냥 맨 앞 PNG만 지워주면 될 거 같다

디버거로 열고 correct 문자열을 찾았다.

문자열 발견

덤프에서도 확인 가능하다

성공
-
31기 황선영 님
문자열 먼저 찾았다.

중간에 저장되어 있는 값이 보인다

umm… 근데 자꾸 Wrong!이 뜬다…
-
31기 이시온 님
문자열 참조를 해보면 Jokermylove가 수상하다(이쯤되면 감이온다)

동적분석하다보면 Jokerlove15가 나온다

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+1Ch] [rbp-24h]
char *lineptr; // [rsp+20h] [rbp-20h] BYREF
size_t n; // [rsp+28h] [rbp-18h] BYREF
FILE *stream; // [rsp+30h] [rbp-10h]
unsigned __int64 v8; // [rsp+38h] [rbp-8h]
v8 = __readfsqword(0x28u);
sub_12EA(a1, a2, a3);
memset(&unk_4050, 0, 8uLL);
memset(&unk_4060, 0, 8uLL);
dword_4070 = 0;
dword_406C = 0;
puts("Enter Your Program");
read(0, &unk_4050, 8uLL);
byte_4058 = 0;
v4 = sub_136D();
printf("Result: %d\n", (unsigned int)v4);
if (v4 > 99999)
{
lineptr = 0LL;
n = 0LL;
stream = fopen("./flag", "r");
getline(&lineptr, &n, stream);
printf("Good, get the flag: %s", lineptr);
free(lineptr);
fclose(stream);
}
return 0LL;
}
main은 여기
- v4>99999 해야함

void makev4()
{
unsigned int v0; // eax
unsigned int v1; // eax
int v2; // [rsp+8h] [rbp-8h]
v2 = 0;
while (1)
{
++v2;
v0 = byte_4050[dword_406C];
if (v0 > 0x72)
goto LABEL_11;
if (byte_4050[dword_406C] >= 0x61u)
break;
if (v0 == 43)
{
++byte_4060[dword_4070];
}
else
{
if (v0 != 45)
goto LABEL_11;
--byte_4060[dword_4070];
}
if (++dword_406C > 7)
dword_406C = 7;
}
v1 = v0 - 97;
if (v1 <= 0x11)
__asm { jmp rax }
LABEL_11:
puts("No Hack!");
exit(-1);
}
v0<=0x72 해야함


v1이 분기의 조건이 되는 거 같은데.. 일단 함수 의도를 모르겠으니 pass
main 다시 뜯어보니까 unsigned100000 초기화 해주는 게 없는데…
내가 rename 하다가 디컴파일 코드가 좀 바뀐 거 같다. 동적 분석으로 넘어가서 봐야할 거 같다.
Description
The auditing department of a software development company received an internal whistleblower report stating that a developer from the development team had outsourced a project to another company for execution. The auditing department initiated an investigation to determine whether there had been a violation of internal labor and security regulations. In response, that developer claimed to have personally developed the project and submitted the program source files and the resulting executable files to the audit team as evidence. The auditing department obtained the previous deliverables (executable files) that the developer had submitted by retrieving them from the company’s project management server. Verify the accuracy of the internal whistleblower’s claims.
#PE #build
1) Write the items indicating the build tool version information stored in the given two PE format executable files in the format of “[ProductID].[BuildID].[Count]”. (80 points)
- Write 9 items per file and do so for both two files. (40 points each)
PE 파일을 이용한건데 어떻게 찾아야 할까 서치를 여러번 해보았다.

여긴 Rich header라는 곳이다. Rich header는 DOS 프로그램 뒤에 위치하며 PE 빌드 환경 등을 담은 메타데이터 header이다. 공식 언급이 없어서인지 CFF explorer에 분석이 안되어 있어서 수기로 해줘야 한다.

이 Rich identifier 뒤 checksum 4바이트로 xor 연산을 해서 복호화(?) 해야 한다.

PE-bear라고 Rich hdr까지 분석해주는 툴이 있었다 (포렌식은 템빨이구나!)

DOS header -> Rich Hdr을 가면 위와 같이 productId, buildId, count가 적혀 있다.
2) Write the build folder paths for the given two executable files. (20 points)
- Write the build folder paths for both files. (10 points each)
원래 exe 파일이라 ida로 열었었는데 pdb 없다는 메세지박스가 떴었다. pdb는 심볼 파일인데 링크를 위해 디버깅 및 프로젝트 상태 정보가 저장되어 있다.
Learn more about PDB files


Debug를 가면 AddressOfRawData를 클릭하면 데이터들이 나오는데 PDB 위치가 build된 exe 파일 위치와 동일하다.