반응형

<출처> http://learder.tistory.com/827146

스마트폰 IMEI 찾기 (프로그램으로 찾아내기)  2010/07/09

 

윈도우에서 GUID 같은 모바일 장치에서도 고유값을 구하는 방법이 필요하다. 무엇으로 할까? 스마트 폰에서는 전화번호, 현재 위치 등도 고유한 값이긴하지만, IMEI 와 IMSI 값이 일반적인 고유치가 될 수 있을 것이다. 일단, IMEI 장비 고유값에 대해서 알아보자.

용어설명
MEID(Mobile Equipment IDentifier) - IMEI의 헥사값(14자), CDMA의 ESN(Electronic Serial Number, 8자)를 대신함
IMSI(International Mobile Subscriber Identity) - GSM/UMTS 에서 모바일 유저의 고유값(15자)
IMEI(International Mobile Equipment Identity) - GSM/WCDMA 및 위성폰에서의 고유값(코드14자+체크용1자=총15자)

우리 나라는 WCDMA 방식을 쓰니까 모바일 장치의 고유값은 IMEI일 것이다.
단, USIM에 대한 고유값 곧 사용자에 대한 고유값은 IMSI이고, 지금 구하는 값은 오직 모바일 장치 고유값에 대한 부분이다.
 
1. 아이폰[각주:1]
애플은 배포본(혹은 외부개발자용), 내부 개발자용으로 헤더 파일을 두개 쓰는 것 같다. 애플 더럽다 퉤퉤.[각주:2]
Message/NetworkController.h 이 헤더 파일에 있는 클래스로 심카드에 관한 정보를 가지고 올 수 있는데, 외부개발자용 헤더에는 IMEI 정보가 없다.

일단, 다음과 같은 헤더 파일을 RealNetworkController.h 를 만들어서
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
/* 
*     Generated by class-dump 3.1.1. * 
*     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard. 
*/
#import "NSObject.h"
@class NSString, NSTimer;
@interface NetworkController : NSObject
{    struct __SCDynamicStore *_store;
    NSString *_domainName;
    unsigned int _waitingForDialToFinish:1;
    unsigned int _checkedNetwork:1;
    unsigned int _isNetworkUp:1;
    unsigned int _isFatPipe:1;
    unsigned int _edgeRequested:1;
    NSTimer *_notificationTimer;
}
+ (id)sharedInstance;
- (void)dealloc;
- (id)init;
- (BOOL)isNetworkUp;
- (BOOL)isFatPipe;
- (BOOL)inAirplaneMode;
- (id)domainName;
- (BOOL)isHostReachable:(id)fp8;
- (id)primaryEthernetAddressAsString;
- (id)IMEI;
- (id)edgeInterfaceName;
- (BOOL)isEdgeUp;
- (void)bringUpEdge;
- (void)keepEdgeUp;
- (void *)createPacketContextAssertionWithIdentifier:(id)fp8;
@end
위 헤더 파일(RealNetworkController.h)를, 개별 프레임워크에 넣고(아래 그림 참조[각주:3])

IMEI 정보가 필요한 부분에 다음 코드를 넣자.
1
2
NetworkController *ntc = [NetworkController sharedInstance];
NSString *imeistring = [ntc IMEI];
imeistring  값이 USIM의  imei 값이다.
단, iOS 4에서 이게 돌아가는지는 모른다. 아이폰이 없다. -.-;

2. 안드로이드폰[각주:4]
1
2
3
4
5
6
7
8
9
10
com.android.internal.telephony.Phone  phone;
String imeistring;
if ( phone.getPhoneName().equals("CDMA"))
{
  imeistring = phone.getMeid();
}
else
{
  imeistring  = phone.getDeviceId();
}
imeistring  값이 USIM의  imei 값이다. 물론 이 방법이 편리하다. 하지만, 배포중인 SDK에서 com.android.internal.telephony.Phone 는 없다.[각주:5] -.-; 다음의 방법을 써야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
import android.telephony.TelephonyManager;
import android.content.Context;
...
String imeistring, imsistring;
  
{
 TelephonyManager telephonyManager;
               
 telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
 imeistring = telephonyManager.getDeviceId();
 imsistring = telephonyManager.getSubscriberId();
}
에뮬레이터는 imei 에서는 000000000000000 imsi 는 310260000000000 을 돌려준다.
윈도우 모바일이나, 심비안과 같이 퍼미션이 필요한데, READ_PHONE_STATE 퍼미션이 필요하단다.
자세한 건 http://developer.android.com/reference/android/telephony/TelephonyManager.html 를 참조하자.

AndroidManifest.xml 에 다음을 넣으면 된단다.
<application> 태그 안에 <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 
3. 윈도우 모바일[각주:6]
IMEI 는 lineGetGeneralInfo 라는 TAPI 함수로 알아낼 수 있다.
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
DWORD GetTapiIMEI(HLINE hLine, CString &a_strIMEI)
{
  DWORD            rc;
  LINEGENERALINFO    LineGeneralInfo;
  TCHAR            lpszSerialNumber[32];
  
  memset( &LineGeneralInfo, 0, sizeof(LineGeneralInfo) );
  LineGeneralInfo.dwTotalSize  = sizeof(LineGeneralInfo);
  rc = lineGetGeneralInfo(hLine, &LineGeneralInfo);
  if (0 == rc)
  {
    if (LineGeneralInfo.dwTotalSize < LineGeneralInfo.dwNeededSize)
    {
      LINEGENERALINFO *lpBuffer = (LINEGENERALINFO *)malloc(LineGeneralInfo.dwNeededSize);
      lpBuffer->dwTotalSize  = LineGeneralInfo.dwNeededSize;
      rc = lineGetGeneralInfo(hLine, lpBuffer);
      memcpy( lpszSerialNumber,
               (TCHAR*)((lpBuffer)+lpBuffer->dwSerialNumberOffset),
               lpBuffer->dwSerialNumberSize );
      free(lpBuffer);
    }
    else
    {
      memcpy( lpszSerialNumber,
               (TCHAR*)((&LineGeneralInfo)+LineGeneralInfo.dwSerialNumberOffset),
               LineGeneralInfo.dwSerialNumberSize  );
    }
    a_strIMEI = lpszSerialNumber;
  }
  return rc;
}
이 함수는 Privileged Function 으로 이를 사용하는 프로그램은 Privilege Certificate 으로 서명을 해야한다.

4. 노키아(심비안 S60 3rd platform)[각주:7]
다음의 헤더 파일과 소스 파일을 만들자.
헤더파일
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
#ifndef __SYSTEM_MANAGER_H__
#define __SYSTEM_MANAGER_H__
#include <etel3rdparty.h>
  
class CystemManager : public CActive{
public:
        typedef enum {EHandsetIMEI, EHandsetIMSI, EHandsetNetworkInfo } InfoType;
  
public:
        static CystemManager* NewL();
        ~CSystemManager();
  
public: // New functions
        void StartL();  // Request
        const TPtrC GetIMEI();
        const TPtrC GetIMSI();
        void GetNetworkInfoL(TUint& aLocation, TUint& aCellId);
private:
        // C++ constructor
        CSystemManager();
        // Second-phase constructor
        void ConstructL();
        // From CActive
        void RunL();
        // Cancel
         void DoCancel();
private:
        enum TGetInfoState {EStart = 1, EGetPhoneInfo, EDone};
  
private:
        InfoType iPhoneInfoType;
        TInt iState; // State of the active object
        CTelephony* iTelephony;
        CTelephony::TPhoneIdV1 iPhoneId;
        CTelephony::TSubscriberIdV1 iSubscriberId;
        CTelephony::TNetworkInfoV1 iNetworkInfo;
        CActiveSchedulerWait iActiveSchedulerWait;
        TBuf<ctelephony::kphoneserialnumbersize> iIMEI;
        TBuf<ctelephony::kimsisize> iIMSI;
        TUint iCellId;
        TUint iLocationAreaCode;
};
#endif // __SYSTEM_MANAGER_H__
소스 파일
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// System includes
#include <badesca.h>
#include <e32std.h>
#include <eikenv.h>
#include <eikappui.h>
#include <eikapp.h>
#include <etelbgsm.h>
  
//User includes
#include "SystemManager.h"
CSystemManager* CSystemManager::NewL()
{
  CSystemManager* self = new (ELeave) CSystemManager();
  CleanupStack::PushL(self);
  self->ConstructL();
  CleanupStack::Pop(self);
  return self;
}
  
CSystemManager::CSystemManager()
 : CActive(EPriorityHigh), // HIGH priority
   iPhoneInfoType(EHandsetIMEI),
   iState(EStart),
   iTelephony(NULL),
   iIMEI(0),
   iIMSI(0),
   iCellId(0),
   iLocationAreaCode(0)
{
}
  
void CSystemManager::ConstructL()
{
  iTelephony = CTelephony::NewL();
  CActiveScheduler::Add(this);
 // Add to scheduler
}
  
CSystemManager::~CSystemManager()
{
  Cancel(); // Cancel any request, if outstanding
  // Delete instance variables if any
  delete iTelephony;
}
  
void CSystemManager::DoCancel()
{
  switch(iPhoneInfoType)
  {
  case EHandsetIMEI:
       iTelephony->CancelAsync(CTelephony::EGetPhoneIdCancel);
      break;
  case EHandsetIMSI:
      iTelephony->CancelAsync(CTelephony::EGetSubscriberIdCancel);
      break;
  default:
      iTelephony->CancelAsync(CTelephony::EGetCurrentNetworkInfoCancel);
      break;
  }
}
  
void CSystemManager::StartL()
{
  Cancel(); // Cancel any request, just to be sure
  iState = EGetPhoneInfo;
  switch(iPhoneInfoType)
  {
  case EHandsetIMEI:
    {
      CTelephony::TPhoneIdV1Pckg phoneIdPckg( iPhoneId );
      iTelephony->GetPhoneId(iStatus, phoneIdPckg);
    }
    break;
  case EHandsetIMSI:
    {
      CTelephony::TSubscriberIdV1Pckg subscriberIdPckg( iSubscriberId );
      iTelephony->GetSubscriberId(iStatus, subscriberIdPckg);
    }
    break;
  case EHandsetNetworkInfo:
    {
      CTelephony::TNetworkInfoV1Pckg networkInfoPckg( iNetworkInfo );
      iTelephony->GetCurrentNetworkInfo(iStatus, networkInfoPckg);
    }
    break;
  }
  SetActive(); // Tell scheduler a request is active
  iActiveSchedulerWait.Start();
}
  
void CSystemManager::RunL()
{
  iState = EDone;
  if ( iActiveSchedulerWait.IsStarted() )
  {
    iActiveSchedulerWait.AsyncStop();
    if(iStatus == KErrNone)
    {
      switch(iPhoneInfoType)
      {
      case EHandsetIMEI:
        iIMEI.Append(iPhoneId.iSerialNumber ); break;
      case EHandsetIMSI:
        iIMSI.Append(iSubscriberId.iSubscriberId ); break;
      case EHandsetNetworkInfo:
        iCellId = iNetworkInfo.iCellId;
        iLocationAreaCode = iNetworkInfo.iLocationAreaCode;
        break;
      }
    }
    else
    {
           // ***********Handle Error here ************
    }
  }
}
  
const TPtrC CSystemManager::GetIMEI()
{
  iPhoneInfoType = EHandsetIMEI;
  iIMEI.Zero();
  StartL();
  TPtrC ptr(iIMEI.Ptr());
  return ptr;
}
  
const TPtrC CSystemManager::GetIMSI()
{
  iPhoneInfoType = EHandsetIMSI;
  iIMSI.Zero();
  StartL();
  TPtrC ptr(iIMSI.Ptr());
  return ptr;
}
  
void CSystemManager::GetNetworkInfoL(TUint& aLocationCode, TUint& aCellId)
{
  iPhoneInfoType = EHandsetNetworkInfo;
  StartL();
  aCellId = iCellId;
  aLocationCode = iLocationAreaCode;
  return;
}
단, "ReadDeviceData" 권한이 있어야 함.

혹은 다음과 같은 방법으로 IMSI를 찾아볼 수도 있단다.[각주:8],[각주:9]
1
RMobilePhone::GetSubscriberId()
 
스마트폰이 없어 테스트를 못했다 -.-; 그럼에도 게시물을 올리는 이유는 이것이 도움 내지 다른 정보를 찾는 시발점이 될 수 있도록 하기 위함이다.

이제 아이폰에서 궁금증은 어떻게 IMSI 값을 찾아낼 수 있는 것인가 이다.

* 2010/07/28에 수정함(안드로이드쪽)

<이상>

반응형

+ Recent posts