1. Frida : Ole에서 개발한 Dynamic Binary Instrumentation (DBI) toolkit
DBI(Dynamic Binary Instrumentation)
DBI는 바이너리 프로그램이 실행되는 동안 명령어를 수정, 삽입하는 것입니다. 이 기술은 프로세스 분석, 추적, 디버깅 등에 사용할 수 있습니다. |
2. Frida 주요기능
Injection | 임의 코드를 메모리나 애플리케이션에 주입합니다. |
Interception | 특정 함수를 후킹하여 매개 변수나 반환 값을 수정하고, 함수를 재작성합니다. |
Stalking | 애플리케이션에서 명령어 레벨의 코드를 추적하여 디버깅하거나 메모리 내의 데이터를 검색, 추출합니다. |
Frida tools 명령어
frida | 앱을 실행하여 frida와 애플리케이션을 연결, 스크립트 로드 |
frida-ps | 프로세스 목록 출력 |
frida-trace | 함수 호출 추적 |
frida-ls-devices | 연결된 기기 목록 출력 |
frida-kill | 동작중인 프로세스 종료 |
Frida 연결
Attach | -F | 현재 foreground에서 실행되고 있는 앱을 연결 |
-n <PACKAGE NAME> | 앱의 패키지 이름으로 앱을 연결 | |
-p <PID> | 앱의 프로세스 ID로 앱을 연결 | |
Spawn | -f <PACKAGE NAME> | 앱을 실행하고, 연결 |
Include/Exclude | -I <MODULE> | 모듈의 모든 함수를 추적 |
-X <MODULE> | 모듈의 모든 함수를 추적에서 제외 | |
-i <MOUDULE!FUNCTION> | 함수를 추적(glob pattern) | |
-x <MODULE!FUNCTION> | 함수를 추적에서 제외(glob pattern) | |
-a <FUNTION!offset> | 함수를 추적(offset) |
애뮬레이터에 연결할 경우 : frida -D emulator-5554 -f com.google.android.calendar --no-pause
Logging | -d, --decorate | 추적할 함수가 속한 모듈이름을 함께 기록 |
Others | -O <TEXT FILE> | 명령줄을 텍스트 파일로 입력 |
Process 사용예시
var modules = Process.enumerateModules()[0];
console.log("modules Base: ", modules.base);
console.log("modules Path: ", modules.path);
var modules_info = Process.findRangeByAddress(ptr(modules.base))
console.log("Protection: ", modules_info.protection);
Module 예시 : findExportByName
var fopen = Module.findExportByName(null, "fopen");
console.log("fopen: " + fopen);
// iOS
var mName = "libsystem_c.dylib"
// Android
var mName = "libc.so"
var fopen_m = Module.findExportByName(mName, "fopen");
console.log("fopen with module: " + fopen_m);
- Int64 / Uint64 : 인자로 전달한 값을 숫자로 변환하는 객체
add(rhs), sub(rhs), and(rhs), or(rhs), xor(rhs) | 변환된 10진수 값에 대해 산술 연산을 수행합니다. |
shr(n), shl(n) | 변환된 10진수 값에 대해 n 비트만큼 쉬프트 연산을 수행할 수 있습니다. |
compare(rhs) | 인자로 전달된 값과 비교합니다. |
toNumber() | Int64 또는 Uint64를 사용한 정수 객체를 숫자 타입으로 변경합니다. |
toString([radix = 10]) | 변환된 10진수의 값을 문자열 형태로 변환합니다. radix는 진수를 나타내며, 기본값은 10입니다. |
- NativePointer: 인자로 전달된 문자열 값을 메모리 포인터로 변환하는 객체
isNull() | 포인터가 NULL인지 검사합니다. |
add(rhs), sub(rhs), and(rhs), or(rhs), xor(rhs) | 포인터 값을 각 함수에 해당하는 산술 연산을 수행합니다. |
compare(rhs) | 전달된 인자 값과 정수 형태로 비교합니다. |
toInt32() | NativePointer 타입을 signed 32-bit 정수로 캐스팅합니다. |
toString([radix = 16]) | 포인터 값을 문자열로 변환합니다. radix는 진수를 나타내며, 기본값은 16입니다. |
readPointer(), writePointer(ptr) | 포인터 주소에서 NativePointer 형태로 값을 읽거나 씁니다. 이 때 포인터의 크기는 대상 프로세스의 포인터 크기로 가정합니다. |
readS8(), readU8(), readS16(), readU16(), readS32(), readU32(), readShort(), readUShort(), readInt(), readUInt(), readFloat(), readDouble(), readS64(), readU64(), readLong(), readULong() | 포인터 주소로부터 각 함수의 접미사를 타입으로 하여 값을 읽습니다. 예를 들어 readU16 함수는 포인터 주소로부터 부호 없는 16비트 값을 읽습니다. |
writeS8(value), writeU8(value), writeS16(value), writeU16(value), writeS32(value), writeU32(value), writeShort(value), writeUShort(value), writeInt(value), writeUInt(value), writeFloat(value), writeDouble(value), writeS64(value), writeU64(value), writeLong(value), writeULong(value) | 포인터 주소에 각 함수의 접미사를 타입으로 하여 값을 씁니다. 예를 들어 writeU16 함수는 포인터 주소에 부호 없는 16비트 값을 씁니다. |
readByteArray(length), writeByteArray(bytes) | readByteArray 함수는 포인터 주소로부터 length 길이만큼 값을 읽고, writeByteArray 함수는 ArrayBuffer 타입의 배열을 포인터 주소에 씁니다. |
readCString([size = -1]), readUtf8String([size = -1]) | 인자로 전달한 size 만큼 문자열을 읽어옵니다. 각 함수에 따라 ASCII, Utf-8 문자열 형태로 읽습니다. size 값이 -1일 경우, NULL 값이 나타날 때까지 읽습니다. |
writeUtf8String(str), writeAnsiString(str) | 인자로 전달한 문자열을 Utf-8, Ansi 문자열 형태로 포인터 주소에 씁니다. writeAnsiString 함수는 Windows 에서만 유효합니다. |
- NativeFunction: 코드에서 지정한 주소를 실제 함수처럼 사용할 수 있는 객체
- new NativeFunction(address, returnType, argTypes[, abi])
반응형
'보안 및 개발 > MOBILE' 카테고리의 다른 글
[iOS] 세팅 트윅 (0) | 2021.11.08 |
---|---|
[Android] Android 7.0 이상 버전 인증서 (0) | 2021.03.22 |
[Android] Burpsuite 사용하기 (0) | 2021.03.18 |
URI Scheme 변조 (0) | 2021.03.05 |
자주사용하는 ADB 명령어 (0) | 2020.11.03 |