분석이나 크랙시 사용할 수 있는 자동화 도구이다.
자동으로 시리얼을 찾아주는 기능 때문에 CTF 리버싱 문제에서 많이 활용된다.
자세히는 github를 보면 다음과 같은 기능을 사용할 수 있다고 설명한다.
angr is a suite of Python 3 libraries that let you load a binary and do a lot of cool things to it:
- Disassembly and intermediate-representation lifting
- Program instrumentation
- Symbolic execution
- Control-flow analysis
- Data-dependency analysis
- Value-set analysis (VSA)
- Decompilation
python 3 라고 설명되어 있지만 python 2.7 버전도 사용할 수 있다.
sudo apt update
sudo apt-get install python3-dev libffi-dev build-essential virtualenvwrapper
pip install angr
angr 설치시 다음과 같은 에러가 발생할 수 있다.
Could not find a version that satisfies the requirement gitdb>=4.0.1
다음의 패키지를 설치하여 해결한다.
pip install gitdb2==2.0.6
pip install GitPython==2.1.14
angr은 내용이 많아 예제를 풀어보며 사용법을 숙지해야 한다.
angr에서 제공하는 기본 사용법을 위한 예제 프로그램이다.
main
에서 유저이름과 패스워드를 입력받고
main
int __cdecl main(int argc, const char **argv, const char **envp) { int v4; // [sp+1Ch] [bp-24h]@1 char v5; // [sp+20h] [bp-20h]@1 char v6; // [sp+28h] [bp-18h]@1 char buf; // [sp+30h] [bp-10h]@1 char v8; // [sp+38h] [bp-8h]@1 v8 = 0; v6 = 0; puts("Username: "); read(0, &buf, 8uLL); read(0, &v4, 1uLL); puts("Password: "); read(0, &v5, 8uLL); read(0, &v4, 1uLL); v4 = authenticate(&buf, &v5); if ( !v4 ) rejected(&buf); return accepted(); }
authenticate
에서 유저이름이 sneaky
에 저장된 값이면 1을 반환한다. 아닐 경우 유저이름에 해당하는 파일을 읽어서 패스워드와 비교한다.
authenticate
signed __int64 __fastcall authenticate(const char *a1, const char *a2) { signed __int64 result; // rax@2 char *s1; // ST00_8@3 char buf; // [sp+10h] [bp-10h]@3 char v5; // [sp+18h] [bp-8h]@1 int fd; // [sp+1Ch] [bp-4h]@3 v5 = 0; if ( !strcmp(a2, sneaky) ) { result = 1LL; } else { fd = open(a1, 0, a2); read(fd, &buf, 8uLL); result = strcmp(s1, &buf) == 0; } return result; }
angr 스크립트를 작성한다.
import angr
import sys
def basic_symbolic_execution():
# execution. First, we load the binary into an angr project.
p = angr.Project('fauxware')
# The entry_state constructor generates a SimState that is a very generic representation of the possible program states at the program's entry point.
state = p.factory.entry_state()
# we have a SimulationManager. SimulationManager is just collections of states with various tags attached with a number of convenient interfaces for managing them.
sm = p.factory.simulation_manager(state)
# Now, we begin execution. This will symbolically execute the program until we reach a branch statement for which both branches are satisfiable.
sm.run(until=lambda sm_: len(sm_.active) > 1)
#These are the constraints that will eventually be passed to our constraint solver (z3) to produce a set of concrete inputs satisfying them.
input_0 = sm.active[0].posix.dumps(0)
input_1 = sm.active[1].posix.dumps(0)
r = None
print input_0
print input_1
basic_symbolic_execution()
fauxware
을 로드한 후, 두 개 이상의 코드흐름을 찾으면 그 때의 입력값을 출력한다.
위 코드를 실행하면 sneaky
에 해당하는 문자열을 확인할 수 있다.
root@hp-virtual-machine:/work/tools/angr/fauxware# python ex.py
WARNING | 2020-05-18 18:46:08,691 | angr.analyses.disassembly_utils | Your version of capstone does not support MIPS instruction groups.
SOSNEAKY
S������
defcamp의 리버싱 문제이다.
main
은 s
에 입력을 받은 뒤, sub_4006fd
의 결과값으로 일치 여부를 출력해준다.
main
signed __int64 __fastcall main(__int64 a1, char **a2, char **a3) { signed __int64 result; // rax@3 __int64 v4; // rcx@6 char s; // [sp+0h] [bp-110h]@1 __int64 v6; // [sp+108h] [bp-8h]@1 v6 = *MK_FP(__FS__, 40LL); printf("Enter the password: ", a2, a3); if ( fgets(&s, 255, stdin) ) { if ( sub_4006FD(&s, 255LL) ) { puts("Incorrect password!"); result = 1LL; } else { puts("Nice!"); result = 0LL; } } else { result = 0LL; } v4 = *MK_FP(__FS__, 40LL) ^ v6; return result; }
sub_4006fd
는 v3
에 저장된 어떤 문자열과 입력값 a1
에 어떤 연산을 한 결과값이 일치하면 1을 반환한다.
sub_4006fd
signed __int64 __fastcall sub_4006FD(__int64 a1) { signed int i; // [sp+14h] [bp-24h]@1 const char *v3; // [sp+18h] [bp-20h]@1 const char *v4; // [sp+20h] [bp-18h]@1 const char *v5; // [sp+28h] [bp-10h]@1 v3 = "Dufhbmf"; v4 = "pG`imos"; v5 = "ewUglpt"; for ( i = 0; i <= 11; ++i ) { if ( (&v3)[8 * (i % 3)][2 * (i / 3)] - *(_BYTE *)(i + a1) != 1 ) return 1LL; } return 0LL; }
스크립트를 작성한다.
import angr
def main():
p = angr.Project("r100")
simgr = p.factory.simulation_manager(p.factory.full_init_state())
simgr.explore(find=0x400844, avoid=0x400855)
return simgr.found[0].posix.dumps(0).strip(b'\0\n')
if __name__ == '__main__':
print(main())
explore
는 find
를 찾고 avoid
를 회피하는 입력값을 찾는다.
0x400844는 일치할 경우, 0x400855는 불일치할 경우 분기하는 주소값이다.
코드를 실행하면 올바른 입력값을 확인할 수 있다.
root@hp-virtual-machine:/work/tools/angr/r100# python ex.py
WARNING | 2020-05-18 20:33:52,912 | angr.analyses.disassembly_utils | Your version of capstone does not support MIPS instruction groups.
Code_Talkers�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������