서론

iOS에서 개발을 하다보면, C <-> Objective C 간 데이터 형변환을 하게되는 경우가 빈번한 것 같다. 예를 들어 C에서의 문자열은 Char * / Char[] 데이터형을 사용하지만, Objective C에서는 NSString객체를 사용한다. 또한 NSString 객체를 Char * / Char[]의 형태로 역변환해서 사용하는 경우도 빈번하기 때문에 경우의 수도 많고, 외워서 쓰기에는 불편함이 있어서 정리해서 모아보는 글을 작성해보기로 하였다.


본론

1 ) unsigned char * ( C ) 와 NSData (Objective C) 

1-1) unsigned char * to NSData

1
2
3
unsigned char * byteString = "byteString";
int byteStringLen = strlen(byteString);
NSData * data = [NSData dataWithBytes:byteString length:byteStringLen];


byteString이 바이트 data라고 가정할 때, 위와 같이 변환 할 수 있다. 만약, 문자열을 바이트 데이터로 바꾸고 싶다면 base64디코딩 과정을 거쳐야 한다.


1-2) NSData to unsigned char *

1
2
NSData * data;
unsigned char* bytesForData = [data bytes];


예시에서는 값이 들어 있지 않지만, NSData형의 data라는 변수에 값이 있을 때 위와 같이 변환 할수 있다.


2 ) char * ( C ) 와 NSString (Objective C)

1-1) char * to NSString

1
2
NSString * nsString = @"Hello Bach!";
char * cString = [nsString UTF8String];


NSString에서 제공하는 UTF8String 메서드는 NSString문자열을 char * 데이터형을 반환해준다.


1-2) NSString to char *

1
2
char * cString = "Hello Bach!";
NSString *nsString = [NSString stringWithUTF8String:cString];


NSString 문자열 변수 또한 NSString에서 제공하는 stringWithUTF8String 메서드를 통해서 char * 데이터형으로 변환할 수 있다.


결론

개인적으로 Objective C에서 프로그래밍을 하면서 느낀점은 C와 같이 혼용해서 사용할 수 있기 때문에 편한 점도 있지만 불편한 점 또한 있고, 그 불편한 점 중에 한가지가 데이터형태가 일치하지 않기 때문에 변환 과정이 필요하다는 것이다. 간단하게 가장 많이 사용하는(본인이) 형변환 두 가지만을 다루었는데, 다른 데이터 형 변환 중 자주 쓰이는 변환이 생긴다면 더 추가할 생각이다.

'Study > iOS' 카테고리의 다른 글

iOS Hooking#2(Frida)  (0) 2017.04.19
iOS Hooking#1(Logos)  (1) 2017.04.18
2장 iOS 해킹 기초 (1)  (0) 2016.06.18
[iOS/GCC] __attribute__((constructor)) / __attribute__((desstructor))  (0) 2016.05.13
[iOS] MD5해시 생성 / SHA256해시 생성  (0) 2016.05.09
서론

 iOS, C/C++에서 main함수보다 먼저 실행하고 싶은 코드가 있을 수 있다. 가령, iOS앱 시작 전에 디바이스 정보를 먼저 얻어온 후 앱을 시작하는 경우나, 사용 Class의 초기화 등의 경우가 이에 해당한다.

 이런 상황에서 __attribute__((constructor)) 옵션을 지정한 함수를 선언하여 해결할 수 있다.


본론

 1. __attribute__((constructor)) 란?

 서론부에서 설명한 것 처럼 GCC에서 제공하는 옵션이다. 이 옵션을 지정한 함수는 main함수 실행 이전에 호출되어 함수의 로직을 수행하게 된다.

 1-1) __attribute__((constructor))을 지정한 함수 선언 방법 예시 

1
2
3
4
void __attribute__((constructor)) before_main( void )
{
/* Things to do before main function */
}


위와 같이 간단한 형태로 정의 할 수 있다.


 1-2) __attribute__((constructor))가 여러개인 경우의 처리

만약 메인 이전에 수행하고싶은 함수의 개수가 두 개 이상인 경우의 처리는 어떻게 하면 될까? 

이러한 경우에는 함수의 Priority를 지정할 수 있는 방법이 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int my_constructor(void) __attribute__((constructor));
int my_constructor2(void) __attribute__((constructor(101)));
int my_constructor3(void) __attribute__((constructor(102)));
int my_constructor(void/* This is the 3rd constructor */
{                        /* function to be called */
    ...
    return 0;
}
int my_constructor2(void/* This is the 1st constructor */
{                         /* function to be called */
    ...
    return 0;
}
int my_constructor3(void/* This is the 2nd constructor */
{                         /* function to be called */
    ...
    return 0;
}


위의 예시 코드에서 볼 수 있듯이 우선순위를 지정하지 않고 default로 선언을 한 경우에는 가장 후 순위로 지정되는 것을 알 수 있으며, 여러 개의 constructor함수가 default선언이 된 경우, 스택 방식으로 먼저 발견되는 함수가 나중에 호출되는 순서를 가진다.


결론

 GCC옵션 중 __attribute__((constructor))를 이용하여 메인 함수 이전에 실행되는 함수를 선언하는 방법에 대해서 알아보았다.

이 옵션과 반대의 옵션인  __attribute__((desstructor)) 옵션을 주면 메인 함수 이후에 실행되는 함수 또한 선언하여 사용 할 수 있고, 이 두 함수의 조합으로 메인 함수 이전과 이후의 처리가 필요한 경우 자유롭게 코드를 더 할 수 있게 된다. 

'Study > iOS' 카테고리의 다른 글

iOS Hooking#2(Frida)  (0) 2017.04.19
iOS Hooking#1(Logos)  (1) 2017.04.18
2장 iOS 해킹 기초 (1)  (0) 2016.06.18
자주쓰는 데이터형 변환  (0) 2016.06.01
[iOS] MD5해시 생성 / SHA256해시 생성  (0) 2016.05.09

서론

 MD5와 SHA256 해시 함수들은 주로 파일의 무결성을 검증하는데 사용되어지고 있는 해시 함수들이며, 이 둘은 단방향 암호화 함수이기 때문에 현재까지는 출력 값을 복호화하여 입력 값을 알아내기는 매우 어렵다.

 이 둘의 원리와 구현방법을 다룬다면 정말 좋겠지만, 이 글에서는 구체적인 알고리즘이나 구현 방법에 대해서는 다루지 않고, 둘의 전반적인 개요와 iOS에서 제공하고 있는 구조체와 함수들을 이용하여 파일과 메모리에 대해서 MD5 및 SHA256 해시 값을 얻어내는 방법에 대해서 알아볼 것이다.


1. MD5 해시란?

 MD5(Message-Digest algorithm 5)는 128비트 암호화 해시 함수이다.  주로 프로그램이나 파일이 원본 그대로인지를 확인하는 무결성 검사 등에 사용된다. 1996년에 MD5의 설계상 결함이 발견되었고, 2004년에는 더욱 심한 암호화 결함이 발견되었고. 2006년에는 노트북 컴퓨터 한 대의 계산 능력으로 1분 내에 해시 충돌을 찾을 정도로 빠른 알고리즘이 발표되기도 하였다. 현재는 MD5 알고리즘을 보안 관련 용도로 쓰는 것은 권장하지 않으며, 심각한 보안 문제를 야기할 수도 있다. 2008년 12월에는 MD5의 결함을 이용해 SSL인증서를 변조하는 것이 가능하다는 것이 발표되었다.


2. SHA256 해시란?

 SHA(Secure Hash Algorithm, 안전한 해시 알고리즘) 함수들은 서로 관련된 암호학적 해시함수들의 모음이다. SHA 함수군에 속하는 최초의 함수는 공식적으로 SHA라고 불리지만, 나중에 설계된 함수들과 구별하기 위하여 SHA-0이라고도 불린다. 2년 후 SHA-0의 변형인 SHA-1이 발표되었으며, 그 후에 4종류의 변형, 즉 SHA-224, SHA-256, SHA-384, SHA-512가 더 발표되었다. 이들을 통칭해서 SHA-2라고 하기도 한다. SHA-256은 256 비트이다.


 간단하게 종합적으로 요약해보면 두 함수 모두 단방향 암호화 해시함수이며, MD5보다는 SHA-2 즉 SHA-256이 더 안전하다고 보인다.


본론

1. 대상 통째로 해시 생성

위의 코드들은 XCode상에서 사용할 수 있는 CommonDigest.h 헤더파일의 일부이다. 위의 함수와 구조체를 이용해서 해시값을 구할 것이다.


결론 부터 이야기 하자면 CC_MD5함수를 이용하여 해시 값을 구할 수 있다.

unsigned char *CC_MD5(const void *source, CC_LONG len, unsigned char *dest)

CC_MD5함수와 CC_SHA256함수는 unsigned char * 를 리턴하고 있으나, 일반적으로 아래 예시처럼 리턴 값을 사용하고 있지 않지만 리턴 값은 md파라미터를 통해 전달된 포인터를 반환한다.

첫 번째 인자 : 해싱 할 데이터의 포인터

두 번째 인자 : 해싱 할 데이터의 사이즈

세 번째 인자 : 해싱 한 데이터를 저장 할 수 있는 포인터

실제 MD5 해싱소스 코드를 보면 아래와 같다. (아래 내용의 MD5를 SHA256으로 치환하고, result의 배열인덱스를 0~ 31 까지 받아내면 SHA256소스가 된다.)

펌 1)

#import <CommonCrypto/CommonDigest.h>

NSString *md5(NSString *str) {
    const char *cStr = [str UTF8String];
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5( cStr, strlen(cStr), result );
    return [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
        result[0], result[1],
        result[2], result[3],
        result[4], result[5],
        result[6], result[7],
        result[8], result[9],
        result[10], result[11],
        result[12], result[13],
        result[14], result[15]
    ];
}

NSString *digest = md5(@"test");
NSLog(@"MD5 TEST %@", digest);


2. 버퍼를 이용하여 해시 생성

 예를 들어 크기가 큰 파일의 해시 값을 구한다고 가정할 때, 위의 방법을 이용하면 파일 전체를 메모리에 올려야 하기때문에 저사양 단말에서는 큰 부담을 가지게 될 수 있다. 따라서 파일의 일부를 버퍼를 이용하여 해시 값을 업데이트함으로써, 메모리사용량을 줄 일 수 있다.


이 때 사용할 구조체 및 함수는

CC_MD5_CTX / CC_SHA256_CTX

CC_MD5_Init() / CC_SHA256_Init()

CC_MD5_Update() / CC_SHA256_Update()

CC_MD5_Final() / CC_SHA256_Final() 를 사용할 것이고, 개념적인 순서로 나열해보면, 

1) CC_MD5_CTX / CC_SHA256_CTX 구조체를 선언 하여 컨텍스트를 선언한다.

2) CC_MD5_Init() / CC_SHA256_Init() 컨텍스트 변수를 초기화한다.

3) CC_MD5_Update() / CC_SHA256_Update()를 이용하여 버퍼의 내용만큼 해시를 업데이트한다.

4) CC_MD5_Final() / CC_SHA256_Final() 모든 과정을 마무리하여 해싱작업을 마친다.


위의 과정을 이용한 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
static int md5useUpdate(char* data, long len, char** outData)
{
    unsigned char hashBuf[BUF_LEN] = {0,};       //업데이트에 사용할 버퍼
    long curSize = 0;                            //현재 까지 읽은 데이터 사이즈
    long remainSize = len;                       //남아있는 데이터 사이즈
    
    //md5 컨텍스트 선언 및 초기화
    CC_MD5_CTX md5;
    CC_MD5_Init(&md5);
    
    //더 읽어야할 데이터가 남아있다면
    while(curSize < len){
        
        if(remainSize < BUF_LEN){       //남은 사이즈가 버퍼보다 작을 경우, 남아있는 사이즈만큼만 메모리 복사 및 해시 업데이트
            memcpy(hashBuf, (char *)data, remainSize);
            CC_MD5_Update(&md5, hashBuf, (CC_LONG)remainSize);
        }
        
        else{                           //일반적인 경우 버퍼사이즈(1024)만큼 메모리 복사 및 해시 업데이트
            memcpy(hashBuf, (char *)data, BUF_LEN);
            CC_MD5_Update(&md5, hashBuf, (CC_LONG)BUF_LEN);
        }
        
        data += BUF_LEN;                //메모리 읽은 후 포인터 이동
        curSize += BUF_LEN;             //현재까지 읽은 사이즈 계산
        remainSize -= BUF_LEN;          //남아있는 사이즈 계산
    }
    
    unsigned char digest[CC_MD5_DIGEST_LENGTH] = {0,};
    CC_MD5_Final (digest, &md5);
    char *tmpOut = *outData;
   
    
    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
    {
        sprintf(tmpOut, "%02x", digest[i]);
        tmpOut += 2;
    }
 
    return 0;
}


 정의한 함수 md5useUpdate는 해싱할 데이터와 사이즈, 해싱한 문자열을 기록할 데이터의 포인터를 입력받는다. 자세한 내용은 주석으로 설명을 대체하였다. 위의 코드 역시 약간의 수정으로 쉽게 SHA256 해싱이 가능 하도록 변환할 수 있다.


결론

 XCode내에서 제공하는 함수를 이용하여 MD5 및 SHA256해시를 생성하는 방법에 대해서 알아보았다. 내부적인 알고리즘까지 깊게 다루지는 못하였지만, 주어진 API를 이용하여 해시를 생성할 수 있었고, 이를 응용할 수 있는 방법은 사용자의 비밀번호를 저장하는 방법에 평문으로 저장하기 보다는 해시 값을 이용하여 저장하고, 파일의 해시 값을 비교하여 파일의 무결성을 검증하는 등 무궁무진할 것 같다. 



'Study > iOS' 카테고리의 다른 글

iOS Hooking#2(Frida)  (0) 2017.04.19
iOS Hooking#1(Logos)  (1) 2017.04.18
2장 iOS 해킹 기초 (1)  (0) 2016.06.18
자주쓰는 데이터형 변환  (0) 2016.06.01
[iOS/GCC] __attribute__((constructor)) / __attribute__((desstructor))  (0) 2016.05.13

1. 애플리케이션 인증서란?

 1) 어플리케이션 인증서는 개발자가 어플리케이션 마켓에 등록할 때 어플리케이션의 신뢰성을 나타내기 위해 사용하는 것이다.   모든 어플리케이션은 설치되기 전에 인증서와 함께 서명되어야 한다.

 2) 안드로이드 어플리케이션 서명은 Jar서명을 수정한 것이며 Jar서명은 암호 해시 함수를 어플리케이션의 구성요소에 적용하는 방식으로 동작한다. 이때 정해진 해시들은 인증서와 함께 배포된다. 인증서는 개발자의 개인키를 이용해서 암호화가 되고, 이것은 서명된 인증서라는 것을 의미한다.


2. apk 압축해제 및 구성요소 확인

 1) apk를 zip으로 확장자를 변경한 후 압축을 해제한다.

 

  2) 공개키 인증서와 서명파일이 존재하는 META-INF폴더로 이동한다.

META-INF 폴더는 어플리케이션 무결성을 성립하는 데 도움이 되기 때문에 매우 중요한 리소스이다. 

MANIFEST.MF :  CERT.SF파일과 매우 유사한 리소스를 정의하고있다. 

CERT.SF : 이 파일은 어플리케이션 서명으로 처리되는 어플리케이션의 모든 리소스를 포함하고 있다. 추가로 Jar암호 서명 공간을 가진다.

펌-1 )

MANIFEST.MF / CERT.SF 의 차이점은 MANIFEST.MF는 각 엔트리(실제 파일)에 대한 SHA1-Digest의  Base64로 인코딩된 값을  나타내고,

CERT.SF는 MANIFEST.MF에 기입되어 있는 각 요소들 예를들어 아래와 같이 "\r\n"를 포함한다.

-----------------------------------------------------------------------------------
Name: res/drawable-xhdpi/ic_launcher.png\r\n
SHA1-Digest: 8nhRYA3Pn75x/54RW/GQ97miQkk=\r\n

\r\n

----------------------------------------------------------------------------------- 
의 Digest의  Base64로 인코딩된 값을  나타낸다.
추가로, CERT.SF의 최상단에 기입되어 있는 SHA1-Digest-Manifest는 MANIFEST.MF의 SHA1-Digest의 Base64로 인코딩된 값을 나타낸다.

///////////////////////////////////////////////

CERT.RSA : X.509 v3 인증서이고, 내부 정보는 keytool에 의해 구조화된다.

- Owner : 공개키 소유자를 정의, 소유자 관련 국가/조직에 대한 기본 정보 포함

- Issuer : 소유의 공개키와 관련된 X.509 인증서의 발급자를 정의

- Serial Number : 발행된 인증서의 식별자

- Valid from ... until : 인증서 관련 속성이 검증되는 기간

- Certificate fingerprints: 인증서의 다이제스트 합, 인증서가 변조되지 않았다는 것을 확인하는데 사용


CERT.RSA파일을 확인하려면 openssl이 설치 되어 있어야한다.

openssl pkcs7 -inform DER -in META-INF/CERT.RSA -noout -print_cert -text

내용은 위와 같다.


3. 재서명할 키스토어 생성

윈도우와 유닉스/리눅스에서 새로운 키스토어를 생성하려면 다음 명령어를 수행한다.

keytool -genkey -v -keystore [name of keystore] -alias [your_keyalias] -keyalg RSA -keysize 2048 -validity [number of days]

keytool은 키스토어에 암호를 설정하도록 도와주며 입력사항은 잊지 않아야한다. 

인증서 생성 중 위와 같은 질의사항이 있고, 과정이 끝나면 키스토어가 생성된다.


4. 생성한 키스토어로 서명

서명전에 재서명할 apk파일에서 위에서 처럼 압축 해제한 후 META-INF를 삭제하고 다시 압축, 확장자 apk로 변경의 과정을 거친다.

jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore [name of your keysotre] [your .apk file] [your key alias]


5. 재서명한 내역 확인

재서명이 제대로 이루어졌는지 확인하기 위해서 apk를 압축해제한 후 openssl로 CERT.RSA파일을 확인한다.


추가 ) jarsigner: unable to sign jar: java.util.zip.ZipException: invalid entry compressed size (expected 766 but got 770 bytes) 등의 에러 메시지가 발생 하는 경우 apk안의 META-INF폴더를 삭제한 후 서명을 시도한다.



펌-1 )의 출처 : http://shloves.tistory.com/entry/CERTSF-MANIFESTMF-%ED%8C%8C%EC%9D%BC%EC%9D%98-%EB%8B%A4%EB%A5%B8%EC%A0%90

 우선 안드로이드 APK 파일은 JAR 와 호환되는 형태로 되어있다. 즉, 코드를 빌드를 하여 나오는 APK  파일은 zip 포맷과 유사한 jar 와도 비슷하다. 특히 서명에 대한 방식은 JAR 와 APK 와 매우 비슷하다. 실제로 서명을 사용해 빌드된 APK 파일 확장명을  zip 으로 변경하고 압축을 풀어보면 META-INF 디렉토리 내부에 이와 관련한 파일이 생성된 것을 볼 수 있다. 

  이 파일은 MANIFEST.MF, CERT.SF, CERT.RSA 파일이다. 


  MANIFEST.MF 파일은 텍스트로 되어있으며 META-INF 디렉토리 내부의 파일을 제외한 APK 패키지 내부 모든 파일(Entry)에 대한 리스트를 보여준다. 각 리스트의 항목에는 파일 경로와 각 파일의 SHA1 해쉬 값(파일을 읽어서 만든다.)을 Base64 로 인코딩 하여 나타내고 있다. 



  CERT.SF 도 마찬가지로 텍스트로 되어있으며, MANIFEST.MF 파일과 구조가 비슷하다. 다만  MANIFEST.MF 에 등록된 각 파일에 대한 해쉬 값은 개인키(APK 빌드할 때 사용되는 인증서, 패스워드와 관련되어 있다고 생각하면 된다) 를 이용하여 SHA1 RSA 로 암호화 한 뒤에 Base64 로 인코딩 되어 있다. 또,  MANIFEST.MF 파일 자체의 위변조를 막기 위하여 MANIFEST.MF 파일의 값도 포함되어 있다. 개인키로 암호화된 값들은 공개키로 복호화할 수 있다. 




 CERT.RSA 는 공개키 인증서이며 소유자, 발행자, 일련번호, 유효기간, 인증서 지문,  알고리즘등이 표시되어 있다.  keeytool 을 이용하여 값을 확인할 수 있다.






   안드로이드 APK 패키지 파일의 서명은 기존 JAVA 에서 제공하는 JAR 파일의 서명과 동일한 방식을 사용한다. 마찬가지로 APK 파일 설치시 JAVA 에서 기본적으로 제공하는 jar 서명 검증 관련 API 를 이용하여 APK 의 무결성을 확인한다. 실제 예를 들자면 Android 내부의  PackageParser 클래스에서도 JarFile 클래스를 이용하여 APK 파일을 열고 내부의 Entry(파일) 리스트를 받아와서 InputStream  을 통하여 파일을 읽는다. 이 과정에서 내부적으로 JarVerifier 클래스가 서명 정보를 확인한다. 서명 정보를 확인할 때, CERT.SF 내부의 암호화된 값들을 공개키를 이용하여 복호화 하고 MANIFEST.MF 의 파일 해쉬 값들과 비교한다. 또, 실제 파일의 SHA1 값들과 MANIFEST.MF 내부의 값들이 일치하는지 검사한다.(순서는 확인하지 못 하였다.) 

   

  이런 과정 덕분에 안드로이드 앱은 빌드시에 사용되는 인증서가 있지 않는한 위변조된 APK 파일은 원본과 구분될 수 있다. 즉, 원본 앱을 업데이트 하는 방식으로 위변조 APK 를 설치할 수 없다. (물론 인증서를 탈취하면 이야기가 달라진다.) 그렇다고 해서 악의적으로 위변조된 APK 파일에 대한 위협이 사라지는 것은 아니다. 만약 원본과 거의 비슷한 모양새로 만들어 빌드하고 다른 인증서로 배포하면 사용자 입장에서는 속아 넘어가서 설치하고 스미싱을 당하거나, 서버와 통신하는 클라이언트 앱의 경우 서버 공격에 대한 빌미를 줄 수 있다. 


  앱이 위변조 되었는지 확인하기 위하여 불가피하게 서버와의 통신이 필요하다. (이 내용은 지금 포스팅과 시리즈로 엮어서 다음 포스팅에서 다루겠다.) 서버로 APK 파일에 대한 고유 키 값을 보내줘야할 때, 인증서 정보(인증서 지문) 만큼 확실한 값은 없다. 


  우선 APK 파일을 압축을 풀면 나오는 META_INF/CERT.RSA  파일에 대한 인증 정보를 확인하는 방법은 다음과 같다.


 keytool -printcert -file  CERT.RSA 

  

  이 명령어를 실행하면 인증서 정보(인증서 지문, Certificate Fingerprint)를 얻을 수 있다.

   

안드로이드 코드상에서 얻는 방법은 다음과 같다. 

Context context = getApplicationContext();
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
String cert = null;
try {
    PackageInfo packageInfo = packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
    Signature certSignature =  packageInfo.signatures[0];
    MessageDigest msgDigest = MessageDigest.getInstance("SHA1");
    msgDigest.update(certSignature.toByteArray());
    cert = Base64.encodeToString(msgDigest.digest(), Base64.DEFAULT);
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}

  

     

  위 코드에서 cert  변수에 이 앱을 빌드할 때 사용된 인증서의 정보 값이 들어간다. 이 값과 APK 파일의 CERT.RSA 파일로부터 'keytool -printcert -file CERT.RSA' 으로 얻은 값을 Base64 로 인코딩 하여 비교하면 동일함을 알 수 있다.  물론 해쉬 알고리즘을 SHA-256 또는 MD5 를 이용하여 요약해도 마찬가지다.  


  하지만, 서버나 외부에서 APK 파일의 인증서 정보 값을 얻을 때는 안드로이드 내부에서 얻는 코드를 이용할 수 없다. 

  그래서 다음과 같은 클래스를 만들어 보았다.   APK 파일의 인증을 검증하는 과정도 포함되어 있으며 잘못된 인증 값을 갖고 있을 경우 예외를 발생시킨다.

  테스트 완료 하였으며, 아마도 안드로이드 내부에서 다른 APK 파일 인증 정보를 읽어오는 것도 가능할 것이다. 


  (코드가 다소 길다. 읽기 쉽도록 소스 파일도 함께 첨부한다.  )

 APKCertExtractor.java



APKCertExtractor:

import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.xml.bind.DatatypeConverter; import kr.re.dev.server.APKCertExtractor.APKCertExtractionException.ErrType; /** * APKCertExtractor * * - APK 파일내의 인증 정보를 SHA1, base64 로 만들어 반환한다. * - APK 파일의 유효성을 검사한다. * - 2개 이상의 서명을 사용하는 APK 파일에 대하여 사용할 수 없다. * 하지만, Play store 에서도 2개 이상의 서명을 갖고 있는 APK 를 허용하지 않는다. * * ::사용법 * - APKCertExtractor.execute(apk 파일 경로); * - 인증 검증 실패시 APKCertExtractionException 예외 발생. * * http://dev.re.kr */ public class APKCertExtractor { public static void main(String[] args) { String cert = ""; try { cert = APKCertExtractor.execute( "test.apk"); } catch (APKCertExtractionException e) { e.printStackTrace(); cert = e.getMessage(); } System.out.println(cert); } /** * 인증서 지문을 반환한다. * @param apkFilePath APK 파일의 경로. * @return * @throws APKCertExtractionException */ @SuppressWarnings("resource") public static String execute(String apkFilePath) throws APKCertExtractionException { try { // JarFile 클래스는 java.util.zip.ZipFile 를 상속받아 구현되었다. // jar 파일에 대한 설명은 다음 블로그 페이지에 자세히 소개되어 있다. // http//www.yunsobi.com/blog/62 // 이 클래스는 jar(zip) 의 엔트리(파일 정보) 및 파일을 읽기 위하여 사용된다. JarFile jarFile = new JarFile(apkFilePath); // APK 파일 내의 AndroidManifest.xml 의 엔트리와 인증 정보를 읽어온다. JarEntry manifestEntry = jarFile.getJarEntry("AndroidManifest.xml"); if(manifestEntry == null) throw APKCertExtractionException.newInstance(ErrType.WrongAPKFormat, apkFilePath, null); Certificate[] certs = loadCertificates(jarFile, manifestEntry); if(certs == null || certs.length == 0) throw APKCertExtractionException.newInstance(ErrType.WrongCert, manifestEntry.getName(), null); Certificate cert= certs[0]; // APK 파일 내의 모든 엔트리의 인증을 검증한다. verifCertificates(jarFile, cert); // 바이트 배열 타입의 인증 정보를 SHA1 Base64로 변환. String hash; try { hash = certToSHA1(cert); } catch (CertificateEncodingException e) { e.printStackTrace(); // 인증서에 문제 있을 때 발생. throw APKCertExtractionException.newInstance(ErrType.WrongCert, apkFilePath, e); } return hash; } // 파일 경로등에 문제 있을 때 발생한다. catch (IOException e) { throw APKCertExtractionException.newInstance(ErrType.ReadFail, apkFilePath, e); } } /** * APK 내부 파일의 인증을 확인한다. * @param jarFile * @param cert */ private static void verifCertificates(JarFile jarFile,Certificate cert) throws APKCertExtractionException { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = (JarEntry) entries.nextElement(); // 서명되지 않는 디렉토리인 META-INF 는 건너뛴다. if (jarEntry.isDirectory() || jarEntry.getName().startsWith("META-INF/") || jarEntry.getName().contains(".DS_Store")) { continue; } Certificate[] certs = null; certs = loadCertificates(jarFile, jarEntry); if(certs == null || certs.length == 0) throw APKCertExtractionException.newInstance(ErrType.WrongCert, jarEntry.getName(), null); Certificate localCert = certs[0]; // 인증 정보가 없는(서명이 되지 않은) 파일 발견. // APK 압축을 풀고 임의의 파일을 넣거나 제거하여 다시 APK 파일로 압축했을 때 발생한다. if (localCert == null) { try { jarFile.close();} catch (IOException e) { e.printStackTrace(); } throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), null); } // 인증 정보가 서로 다른 엔트리 발견. // 물론 설치는 안 되겠지만, 위변조 시도되는 앱으로 의심된다. else if (!cert.equals(localCert)) { try { jarFile.close();} catch (IOException e) { e.printStackTrace(); } throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), null); } } } /** * Certificates 객체를 반환한다. * @throws APKCertExtractionException */ private static java.security.cert.Certificate[] loadCertificates(JarFile jarFile, JarEntry jarEntry) throws APKCertExtractionException { if(jarEntry == null || jarFile == null) return null; try { // JarEntry 로부터 Certificate 객체를 얻기 위해서는 JarEntry 를 검증하기 위하여 끝까지 다 읽어야 한다. // 이 과정에서 내부적으로 JarVerifier 클래스를 통하여 인증에 대한 검증이 이뤄어진다. byte[] buffer = new byte[1024]; InputStream is = jarFile.getInputStream(jarEntry); try { while (is.read(buffer, 0, buffer.length) != -1) {} // 테스트 결과 MATA-INF 폴더의 파일 내에 해당 엔트리의 인증값들은 있지만, // 실제 파일이 존재하지 않을 경우 아래 예외가 발생한다. // 역시 위변조된 앱일 가능성이 크다. // 물론 이 경우도 일반적인 안드로이드 폰에서는 설치조차 되지 않는다. } catch(SecurityException e) { throw APKCertExtractionException.newInstance(ErrType.ForgeryAPK, jarEntry.getName(), e); } is.close(); buffer = null; return (java.security.cert.Certificate[])jarEntry.getCertificates(); } catch (IOException e) { System.err.println("Exception reading " + jarEntry.getName() + " in " + jarFile.getName() + " " + e); } return null; } /** * 서명 깂을 SHA1 해쉬로 변경하여 Base64 로 만든 String 값으로 반환한다. * @param cert * @return * @throws CertificateEncodingException */ private static String certToSHA1(Certificate cert) throws CertificateEncodingException { byte[] certWith = null; // X509 인증 정보를 ASN.1 DER 구조의 byte 배열로 반환한다. certWith = cert.getEncoded(); MessageDigest md = null; try { // 인증 정보를 SHA1 해쉬로 변경. md = MessageDigest.getInstance("SHA1"); md.update(certWith); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return DatatypeConverter.printBase64Binary(md.digest()); } /** * 인증서 추출 예외상황. * @author ice3x2 */ public static class APKCertExtractionException extends Exception { private static final long serialVersionUID = 6796836839897143903L; private ErrType mError; protected static APKCertExtractionException newInstance(ErrType errType, String path, Throwable throwable) { String message = ErrType.toMessage(errType) + " (" + path + ")"; APKCertExtractionException apkCertExtractionException = (throwable == null)?new APKCertExtractionException(message):new APKCertExtractionException(message, throwable); apkCertExtractionException.mError = errType; return apkCertExtractionException; } private APKCertExtractionException(String message, Throwable throwable) { super(message, throwable); } private APKCertExtractionException(String message) { super(message); } /** * 에러 타입을 반환한다. * @return */ public ErrType getError() { return mError; } public static enum ErrType { /** * 잘못된 APK 파일 포맷. */ WrongAPKFormat("Wrong APK file format."), /** * APK 파일을 읽을 수 없음. */ ReadFail("APK file read failed."), /** * 잘못된 인증서. 파일에 대한 인증 정보가 존재하지 않는다. */ WrongCert("Wrong certificate. Certificate verified failed."), /** * 위변조가 의심되는 APK. 인증 정보 검증에 문제가 있다. */ ForgeryAPK("Wrong certificate. This package is suspected with forgery apk."); private String message; private ErrType(String value) { message = value; } protected static String toMessage(ErrType errType) { return errType.message; } } } }



출처 : Dev.re.kr (http://dev.re.kr/70)

안드로이드 최신 버전의 SDK에서 pm명령어 모음입니다. adb shell로 접근하여 얻을 수 있는 명령어입니다. adb shell 접근 방법은 아래에서 설명하겠습니다.

명령어를 통해 앱을 설치하거나, 삭제하고, 사용자를 추가하고, 삭제 할 수 있는 명령어도 있습니다. 현재 기본값으로 셋팅 된 설치 경로를 가져오는 get-install-location 도 있습니다. 일반적으로 [0/auto]가 설정되어 있습니다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
usage: pm list packages [-f] [-d] [-e] [-s] [-e] [-u] [FILTER]
       pm list permission-groups
       pm list permissions [-g] [-f] [-d] [-u] [GROUP]
       pm list instrumentation [-f] [TARGET-PACKAGE]
       pm list features
       pm list libraries
       pm path PACKAGE
       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] PATH
       pm uninstall [-k] PACKAGE
       pm clear PACKAGE
       pm enable PACKAGE_OR_COMPONENT
       pm disable PACKAGE_OR_COMPONENT
       pm disable-user PACKAGE_OR_COMPONENT
       pm set-install-location [0/auto] [1/internal] [2/external]
       pm get-install-location
       pm createUser USER_NAME
       pm removeUser USER_ID
  
pm list packages: prints all packages, optionally only
  those whose package name contains the text in FILTER.  Options:
    -f: see their associated file.
    -d: filter to only show disbled packages.
    -e: filter to only show enabled packages.
    -s: filter to only show system packages.
    -3: filter to only show third party packages.
    -u: also include uninstalled packages.
  
pm list permission-groups: prints all known permission groups.
  
pm list permissions: prints all known permissions, optionally only
  those in GROUP.  Options:
    -g: organize by group.
    -f: print all information.
    -s: short summary.
    -d: only list dangerous permissions.
    -u: list only the permissions users will see.
  
pm list instrumentation: use to list all test packages; optionally
  supply <target-package> to list the test packages for a particular
  application.  Options:
    -f: list the .apk file for the test package.
  
pm list features: prints all features of the system.
  
pm path: print the path to the .apk of the given PACKAGE.
  
pm install: installs a package to the system.  Options:
    -l: install the package with FORWARD_LOCK.
    -r: reinstall an exisiting app, keeping its data.
    -t: allow test .apks to be installed.
    -i: specify the installer package name.
    -s: install package on sdcard.
    -f: install package on internal flash.
  
pm uninstall: removes a package from the system. Options:
    -k: keep the data and cache directories around after package removal.
  
pm clear: deletes all data associated with a package.
  
pm enable, disable, disable-user: these commands change the enabled state
  of a given package or component (written as "package/class").
  
pm get-install-location: returns the current install location.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media
  
pm set-install-location: changes the default install location.
  NOTE: this is only intended for debugging; using this can cause
  applications to break and other undersireable behavior.
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media
</target-package>

 

 

이 중 가장 많이 사용하는 명령어가 앱을 강제로 SD카드로 이동하는 명령어 일 겁니다. 해당 명령어도 아래와 같이 변경되었습니다.

설치 경로의 기본 값을 가져오는 명령어와 설정하는 명령어입니다.

 

pm get-install-location //기본값으로 지정된 설치 경로를 불러옵니다.
pm set-install-location 0/1/2 //강제로 경로를 설정합니다.

 

 

[adb를 이용한 apk 파일 추출 및 인스톨]

 

c:\> adb shell


# pm list packages -f

...

package:/system/app/SettingsProvider.apk=com.android.providers.settings
package:/system/app/TtsService.apk=android.tts
package:/system/app/Mms.apk=com.android.mms << 이걸 가져오고 싶으면...
package:/system/app/MediaProvider.apk=com.android.providers.media
package:/system/app/CertInstaller.apk=com.android.certinstaller
package:/system/app/DownloadProvider.apk=com.android.providers.downloads

...

 

# exit

 

c:\>adb pull /system/app/Mms.apk .

adb pull (가져올 파일 경로/파일명.apk) (받을 경로/파일명.apk)




출처 : 코드 클리핑 (http://www.dreamy.pe.kr/zbxe/CodeClip/163974)

1. Man In The Middle Attack?

  중간자 공격(man in the middle attack, MITM)은 네트워크 통신을 조작하여 통신 내용을 도청하거나 조작하는 공격 기법이다. 중간자 공격은 통신을 연결하는 두 사람 사이에 중간자가 침입하여, 두 사람은 상대방에게 연결했다고 생각하지만 실제로는 두 사람은 중간자에게 연결되어 있으며 중간자가 한쪽에서 전달된 정보를 도청 및 조작한 후 다른 쪽으로 전달한다.


  일반적으로 가정에서 흔히 구성되고 있는 네트워크 형태로는 하나의 인터넷 회선에 공유기를 사용하여 홈 네트워크를 구성하고 있다. 이 때, 공격자가 같은 홈 네트워크를 사용하고 있다고 가정하면, 위 그림과 같은 중간자 공격을 수행 할 수 있다. 피해자 입장에서는 공유기를 통해서 인터넷과 연결되고 생각 할 수 있지만, 실제로는 공격자를 통해 공유기를 거쳐 인터넷을 사용 하고 있는 것이고, 공격자는 피해자의 패킷들을 감청 및 조작이 가능하다.

  이 글에서는 ARP 스푸핑을 통해 MITM을 수행하는 모습을 보여줄 것이다.

실습 환경은 공격자 PCOSKali linux 2.0, 피해자 OSWindows 7을 사용하였다. APiptime 104N모델 펌웨어 9.58버전을 사용하였다.

 

2. ARP Spoofing

  ARP 스푸핑(ARP spoofing)은 근거리 통신망(LAN) 하에서 주소 결정 프로토콜(ARP) 메시지를 이용하여 상대방의 데이터 패킷을 중간에서 가로채는 중간자 공격 기법이다. 이 공격은 데이터 링크 상의 프로토콜인 ARP 프로토콜을 이용하기 때문에 근거리상의 통신에서만 사용할 수 있는 공격이다.

 이 기법을 사용한 공격의 경우 특별한 이상 증상이 쉽게 나타나지 않기 때문에 사용자는 특별한 도구를 사용하지 않는 이상 쉽게 자신이 공격당하고 있다는 사실을 확인하기 힘들다.

  ARP(Address Resolution Protocol, ARP)는 네트워크 상에서 IP 주소를 물리적 네트워크 주소로 대응(bind)시키기 위해 사용되는 프로토콜이다. 여기서 물리적 네트워크 주소는 이더넷 또는 토큰링의 48 비트 네트워크 카드 주소를 뜻한다.

 

3. arpspoof tool 사용 

victimip192.168.0.3이고, 네트워크의 gatewayip192.168.0.1,

공격자의 ip192.168.0.2이다. 

arpspoof 사용법 : arpspoof -i [interface 설정] -t [victim ip] [위장 할 ip] 

arpspoof -i wlan0 -t 192.168.0.3 192.168.0.1

 먼저, 피해자 pc에 내가 게이트웨이인 것처럼 위장하도록 한다.

 그 다음, 같은 방식으로 arpspoof를 이용해 게이트웨이에게 내가 피해자의 pc인 것처럼 위장한다.

arpspoof -i wlan0 -t 192.168.0.3 192.168.0.1

  이렇게 설정하면 위의 그림과 같은 가짜 경로를 만들어 낼 수 있으며, 실제로 피해자의 컴퓨터에서 arp -a 명령어를 통해 arp테이블을 보면 게이트웨이 ip주소와 공격자 컴퓨터의 맥주소가 같은 것을 볼 수 있다.

 

 피해자와 게이트웨이로부터 받은 패킷을 서로에게 ip forwarding 해주기 위하여 echo 1 > /proc/sys/net/ipv4/ip_forward 로 설정을 해준 후 fragrouter툴을 사용하여 ip 포워딩을 해서 연결을 완성해준다.

 

fragrouter -B1

 이 후, WireShark를 이용하여 피해자의 인터넷 패킷을 감청 할 수 있으며,

필터에 ip.addr == 192.168.0.3(피해자의 ip주소) 입력하여 필터링을 해주어 쉽게 볼 수 있다.

 

  아래의 그림은 한국기술교육대학교 온라인교육 홈페이지에 로그인 할 때의 패킷이고, 해당 홈페이지는 아이디와 비밀번호를 Plain text로 전달하여 다 보이게 된다.




1 . 정적분석

 

1) VirusTotal

분석 전 실습파일(Lab03-03.exe)를 VirusTotal(www.virustotal.com)에서 검사를 해본 결과

탐지 비율이 37/54로써 악성 코드임을 짐작할 수 있었다.


2) PEview와 Depandency Walker



 

 PEview와 Depandency Walker Lab03-03.exe PE구조에서 kernel.dll밖에 임포트하지 않는 것으로 보여지지만,

GetProcAddress 함수를 사용하는 것으로 보아서는 다른 dll도 임포트하지 않을까 라는 추측을 했고

WriteFile함수를 보고 어떠한 파일에 영향을 미치는 일을 할 것 같다는 생각을 했다.


3) PEiD

혹시 패킹이 된것은 아닐까 싶어 PEiD를 사용하여 분석해보았지만 

Visual C++ 6.0으로 컴파일 된 것 외에 다른 정보는 얻을 수 없었다.


 4) strings

전에 하던 정적 분석에서는 strings를 사용해 유용한 문자열을 많이 뽑아 볼 수 있었지만,

이번 파일은 그다지 유용한 문자열이 없었다. 거의 텍스트가 깨지거나 의미없는 특수기호 대문자 AAAAAAAAAAAA의 

연속이였고 그나마 의미있어보이는 문자열을 캡쳐해보았다. 물론 저게 무엇을 의미하는지는 잘 모르겠다.



 

정적 분석 결과 

1. virus total을 이용한 조회 결과는 악성코드로 보는 것이 맞는 것 같다.

2. 어떠한 방식인지는 모르겠지만 파일에 영향을 주는 일을 할 것같다.

-----------------------------------------------------------------------이외엔 정적 분석으로 별 소득이 없었다. 

 

2. 동적분석

1) Process Monitor

Lab03-03.exe를 실행 하면, svchost.exe라는 자식 프로세스를 생성 한 후에 

Lab03-03.exe프로세스는 종료되어진다.


생성된 자식 프로세스 svchost.exe는 Lab03-03.exe프로세스 종료 이후 고아 프로세스가 된다.

(책에서 고아 프로세스라고 설명을 하던데 원래 있는 말인지는 모르겠지만 잘 이해할 수있게 표현 한것 같다.)

svchost.exe는 아래와 같이 services.exe아래에 자식 프로세스로 생성 되는 것이 일반적이기 때문에

고아프로세스로 실행 되고 있는 svchost.exe프로세스는 이상하다고 생각 된다.

고아가 된 svchost.exe프로세스의 속성을 확인하던 중 [ENTER]와 [TAB]과 같은 문자열을 발견했는데,

보통 키로거에서 많이 볼 수 있는 문자열이라고 한다.

또한 practicalmalwareanalysis.log이라는 로그파일(?)의 이름처럼 보이는 문자열도 찾았다.

(키로거는 사용자의 키 값을 서버에 전송하거나, 키의 값을 파일로 저장하여 그 파일을 전송하는 악성코드의 종류인데,

아마 숫자 영문키 외에 엔터, 쉬프트, 컨트롤 등과 같은 키값을 저장하기 위해 만들어놓은 문자열이 아닌가 추측한다.) 


2) ProcessMonitor


 Process Explorer를 통해 수상한 프로세스를 찾았고, 그 프로세스의 pid는 3412였고, 

ProcMon에 필터를 PID로 두고 분석을하였다.

 분석을 한 결과 

Process Profiling

CreateFile

QueryStandardInformationFile

WriteFile (*n번)

CloseFile

IRP_MJ_CLOSE


1. Process Profiling을 하면서 키이벤트가 발생하는 것을 감지하고

2. CreateFile, QueryStandardInformationFile, WriteFile의 과정으로 파일을 열고 키값을 저장

3. CloseFile, IRP_MJ_CLOSE를 통해 파일입출력을 닫는다.

이러한 과정을 통해 키로깅을 하고 있는 것 같다.

파일의 저장경로는 옆에 나와있듯이 Lab03_03.exe가 위치한 폴더 내이고,

파일 명은 Process Explorer에서 찾아낸  practicalmalwareanalysis.log였다.


파일을 메모장으로 열어보니, 어떤 윈도우창에서 어떤 키를 조작했는지 까지 상세하게 나와있었다.

끝:)

------------------------------------------------------------------------------------------------------------

전에 했던 분석에서는 HTTP GET방식을 이용해 통신기능을 수행해서 탐지 시그니처를 정할 수 있었는데, 

이러한 경우엔 파일의 고유 값 외에 어떤 것을 탐지 시그니처로 정해야할지 아직 잘 모르겠다. ㅠ 

그래도 이번 분석은 책의 도움을 많이 받지 않고 한 것에 만족한다. 

 

'Study > ETC' 카테고리의 다른 글

Google I/O 2016 Extended Seoul 정리 및 후기  (4) 2016.06.19
Arpspoofing  (0) 2015.10.16
실전 악성코드와 멀웨어 분석 Lab03-02.dll  (0) 2014.08.29
실전 악성코드와 멀웨어 분석 Lab03-01.exe  (0) 2014.08.11
code engn 04  (0) 2014.04.04

1 . 정적분석

 

1) VirusTotal

분석 전 실습파일(Lab03-02.dll)VirusTotla(www.virustotal.com)에서 검사를 해본결과 

탐지 비율이 41/54로써 거의 악성코드임을 짐작할 수 있었다.

 

2) PEview




 PEviewLab03-02.dllPE구조에서, ws2_32.dllwinnet.dll을 임포트하며

사용하는 함수들을 보면 통신기능을 수행할 것으로 짐작돼는 internethttp관련 함수들이 보여서

어떠한 데이터를 외부로 전송하거나, 외부로 부터 악성코드를 다운받을 것이라 짐작되어진다.


 5) strings

strings로 찍어보면 practicalmalwareanalysis.com 이라는 서버네임같이 보이는 문자열도 보이고,

GET, HTTP/1.1이라는 문자열을 보아 유추해보면 80포트를 이용하여 httpget방식으로 사용할 것이라고 추측해 볼수 있었다. installA로 설치가 가능 할 것으로 예상된다.

 

정적 분석 결과 

1. 외부와 httpget방식을 이용하여 통신하여, 데이터를 전송하거나 악성코드를 다운받을 것이다. 2. installA라는 문자열을 통해 이것으로 설치를 할 수 있을 것 같다.

3. virus total을 이용한 조회 결과는 악성코드로 보는 것이 맞는 것 같다.

 


 

2. 동적분석

1) rundll32.exe Lab03-02.dll,installA

dll자체는 실행 파일이 아니므로 단독으로 실행하여 분석을 진행 할 수 없으나,

rundll32.exe을 통해 해당 dll의 함수를 실행 시켜 분석을 계속 진행 해 볼 수 있었다.

(dllinstallA실행 전후 관찰을 위해 rundll32실행 전 Process ExplorerRegshot을 미리 셋팅해두었고, 이 후 변화를 분석하였다.)

 

2) Regshot 




키가 추가된 위치를 보면 악성코드가 서비스 IPRIP를 이용해 설치 했다는 것을 볼 수 있다.

또한 악성코드가 dll이므로 자체 실행이 아닌 dll을 로드할 실행 파일이 필요한데 이것은 imagepath를 보면 알 수있다.

ImgePath는 헥사코드로 인코딩이 되어 있어서 디코딩을 하여 확인을 한 결과 

svchost.exe 프로세스 내에서 실행 되는 것을 확인 하였다.

(그 외 DisplayName이나 Description항목은 악의적인 서비스를 식별하는데 사용가능한 흔적을 생성한다. 라고 책에 설명되어 있는데, 아직은 잘 이해 하지 못하겠다.)


3) regedit



실행 -> regedit을 실행하여 실제 레지스트리에 저렇게 등록 되어 있는지 확인해 보았더니, regshot 보고서에서 보았던 

내용이 그대로 등록되어있음을 확인 할 수 있었다.


4) Process Explorer


Process Explorer를 통해 실제 Lab03-02.dll을 로드하여 실행되고있는 svchost를 구별하기 위해 검색해 보았고, 1104라는 pid를 가진 scvhost프로세스라는 것을 찾았다! 

(서비스에 Intranet Network Awaeness(INA+)[IPRIP]라는 항목도 보인다.)




5) 악성코드의 실행


ApateDNS를 통해서 모든 요청을 나 자신으로 돌아오게 만들고 넷켓을 이용하여 80포트로 들어오는 요청을 받는다.

특별히 80포트를 조회한 이유는 정적분석(strings)을 통하여 http의 GET방식을 사용 할 것이라 추측하였기때문이다.

그 결과 예상대로 http의 GET방식을 사용한 요청이 들어왔으며 host역시 정적분석 문자열을 통하여 확인했던

practicalmalwareanalysis.com 이였다.

여기서 악성코드의 시그니쳐를 뽑아 내자면,

첫째로 serve.html이 http요청을 하고있기 때문에 이것을 시그니쳐로 GET요청을 사용 할 수 있고,

두 번째로 User-Agent에서 사용자 컴퓨터명을 제외한 Windows XP 6.11이 일정하기때문에 시그니쳐로 사용 할 수 있다. 

:) 끝

____________________________________________________________________________________________________________

사실 아직까지는 책과 인터넷 검색을 통해서 따라하는 수준이고, 익숙치가 않다. 마지막 결과를 받는 부분에서 제대로 

악성코드를 설치하고 실행을 했음에도 결과값이 나오지않다가 어쩌다가 결과 값을 받게 되었다. 요청이 찍히지 않았는지, 왜 갑자기 요청이 들어오게 되었는지 잘모르겠지만, 하나하나 따라하다보면 결국엔 혼자서도 잘 할 수 있을 거라 믿고

열심히 진행해야겠다.


 

'Study > ETC' 카테고리의 다른 글

Arpspoofing  (0) 2015.10.16
실전 악성코드와 멀웨어 분석 Lab03_03.exe  (0) 2014.09.01
실전 악성코드와 멀웨어 분석 Lab03-01.exe  (0) 2014.08.11
code engn 04  (0) 2014.04.04
IA-32 Register 기본 설명 - 1  (0) 2013.10.08

1 . 정적분석


1) VirusTotal

분석 전 실습파일(Lab03-01.exe)을 VirusTotla(www.virustotal.com)에서 검사를 해본결과 

탐지 비율이 50/54로써 거의 악성코드임을 알수 있었다.


2) PEview

PEview로 Lab03-01.exe의 PE구조를 살펴 보니, 임포트한 dll은 kernel32.dll밖에 보이지 않았으며 별다른 점은 보이지 않았다.


3)Depandancy Walker

Depandancy Walker에서 살펴보아도 임포트한 dll은 kernel32.dll, 사용한 함수는 ExitProcess밖에 보이지 않는다.

이 점을 미루어보아 무언가로 패킹되었다는 것을 짐작 할 수 있었다.


4)PEiD


PEiD로 확인해 보니 예상대로 패킹이 되어있었고, PEncrypt로 패킹이 되어있었다. 


5) strings

strings로 찍어보면 www.practicalmalwareranalysis.com이라 던가 어떤 레지스트리 경로처럼 보이는 것도 나오고, 

의심스러운 파일명하며, 단서가 될만한 것들이 찍혀보이는 것을 확인했다.


정적 분석 결과 

PEncrypt로 패킹이 되어 있으며, 도메인네임이 있는 것으로 보아 통신기능(어떠한 파일을 다운을 받거나, 데이터를 빼내는 기능 등)을 하는 것 같다는 것을 유추해 볼 수 있다. 또한 무슨일을 하는지는 모르지만 VirusTotal의 결과로 사용자에게 피해를 주는 악성코드라는 것을 알게 되었다.


2. 동적분석

1)Process Explorer

프로그램 실행 전 Process Explorer를 실행하고 실습파일을 실행하였고, 


어떤 dll을 로드하는지 확인해보니 ws2_32.dll과 wshtcpip.dll과 같은 dll을 로디해서 사용하는것을 확인, 고로

통신기능을 수행할 것으로 생각 할 수 있다.


2)Process Monitor

위의 정적분석 (strings의 결과)를 거쳤을때, 어떠한 파일경로로 의심되는 문자열이 있었고, 레지스트리경로로 보이는 문자열이 있었기 때문에, 필터설정을 위의 그림과 같이 Process Name은 Lab03-01.exe, Operate는 writeFile과 RegSetValue를 필터링 하여 살펴본 결과가 나왔다. 

먼저, WriteFile의 결과로 나온것을 더블클릭하여 자세히보니,  c:\WINDOWS\system32의 경로에 

크기가 7168byte짜리인 vmx32to64.exe라는 파일을 생성하고 있다는 것을 알 수 있었다.

이 파일을 다시 VirusTotal에 올려서 검사를 해보았더니

아래 처럼 사실 파일의 이름만 바뀌었을 뿐, Lab03-01.exe와 같은 파일임을 알수 있었다. 위의 SHA256키가 같고,

다른 분들이 분석한 것을 보면 파일의 md5해시값도 또한 같다고 한다.

이 점을 보면 Lab03-01.exe를 실행하면 c:\WINDOWS\system32의 경로에 vmx32to64.exe라는 이름으로 바꾸어 

복사 생성한다는 것을 알 수있다.


다시 ProcMon의 결과로 돌아와서 이번엔 RegSetValue의 결과를 상세히 확인하면, 복사생성한 vmx32to65.exe를 

레지스트리에 등록하여 윈도우 시작시 프로그램이 자동 시작되도록 값을 셋팅하는 것을 알 수 있다. 

시작프로그램에서 역으로 제거하는 방법은 

실행창에서 regedit을 실행 -> 레지스트리 관리창을 띄우고

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run에서 해당 정보를

(여기서는 VideoDriver라는 이름으로 되어있다. 실제 실행되는 파일은 vmx32to64.exe이다.) 삭제한다.


3)ApateDNS

역시 정적분석(Strings)결과 도메인네임으로 보이는 문자열이 보였기때문에, 또한 Process Explorer의 결과로 

통신기능을 지원하는 dll을 로드하므로 통신기능을 수행할 것 같다는 짐작을 할 수 있었으므로, ApateDNS로 확인해보았더니 문자열에서 보았던 주소로 어떠한 요청을 보내고 있다는 것을 확인할 수 있었다.


따라서 요청하는 IP를 나에게 다시 돌아오게 만들고, netcat을 이용, 80번 포트(http서비스)와 443번 포트(https서비스)를 리스닝을해놓고 기다려보니 80번 포트는 아무것도 오지않았고, 443포트에서 알수 없는 외계어가 들어왔다.

확실히 어떠한 것을 보내고 있는지는 모르지만 해당 서버로 어떠한 데이터를 주기적으로  데이터를 전송하고 있었다.


최종결론

실습파일(Lab03-01.exe)은 실행시 악성코드작성자가 코딩한 경로로 이름을 변경한(vmx32to64.exe) 자기자신을 복사 생성하며, 동시에 레지스트리에 그 복사 파일을 등록하여 윈도우시작시 자동 실행되어지게 만들었다.

또한 어떠한 내용인지 정확히 밝혀지지는 않았지만 www.practicalmalwareanalysis.com으로 주기적으로 https를 이용 어떠한 데이터를 전송하고 있는 것으로 분석 되었다.







+ Recent posts