티스토리 뷰
윈도우에서 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이고, 지금 구하는 값은 오직 모바일 장치 고유값에 대한 부분이다.
IMSI(International Mobile Subscriber Identity) - GSM/UMTS 에서 모바일 유저의 고유값(15자)
IMEI(International Mobile Equipment Identity) - GSM/WCDMA 및 위성폰에서의 고유값(코드14자+체크용1자=총15자)
우리 나라는 WCDMA 방식을 쓰니까 모바일 장치의 고유값은 IMEI일 것이다.
단, USIM에 대한 고유값 곧 사용자에 대한 고유값은 IMSI이고, 지금 구하는 값은 오직 모바일 장치 고유값에 대한 부분이다.
애플은 배포본(혹은 외부개발자용), 내부 개발자용으로 헤더 파일을 두개 쓰는 것 같다. 애플 더럽다 퉤퉤. 2
Message/NetworkController.h 이 헤더 파일에 있는 클래스로 심카드에 관한 정보를 가지고 올 수 있는데, 외부개발자용 헤더에는 IMEI 정보가 없다.
Message/NetworkController.h 이 헤더 파일에 있는 클래스로 심카드에 관한 정보를 가지고 올 수 있는데, 외부개발자용 헤더에는 IMEI 정보가 없다.
일단, 다음과 같은 헤더 파일을 RealNetworkController.h 를 만들어서
/* * 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
IMEI 정보가 필요한 부분에 다음 코드를 넣자.
NetworkController *ntc = [NetworkController sharedInstance]; NSString *imeistring = [ntc IMEI];
imeistring 값이 USIM의 imei 값이다.
단, iOS 4에서 이게 돌아가는지는 모른다. 아이폰이 없다. -.-;
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
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" />
AndroidManifest.xml 에 다음을 넣으면 된단다.
<application> 태그 안에 <uses-permission android:name="android.permission.READ_PHONE_STATE" />
3. 윈도우 모바일 6
IMEI 는 lineGetGeneralInfo 라는 TAPI 함수로 알아낼 수 있다.
IMEI 는 lineGetGeneralInfo 라는 TAPI 함수로 알아낼 수 있다.
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 으로 서명을 해야한다.
다음의 헤더 파일과 소스 파일을 만들자.
헤더파일
#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__
소스 파일
// 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; }
RMobilePhone::GetSubscriberId()
스마트폰이 없어 테스트를 못했다 -.-; 그럼에도 게시물을 올리는 이유는 이것이 도움 내지 다른 정보를 찾는 시발점이 될 수 있도록 하기 위함이다.
이제 아이폰에서 궁금증은 어떻게 IMSI 값을 찾아낼 수 있는 것인가 이다.
* 2010/07/28에 수정함(안드로이드쪽)
- http://stackoverflow.com/questions/823181/how-to-get-imei-on-iphone [본문으로]
- 겉보기에는 애플은 사용자 정보를 노출하지 않기를 원하는 것 같아보인다. 사실 구조상 완전히 막을 수는 없음과 똑같은 클래스내에 인터페이스만 막는 얍삽한 방법을 쓴걸 보면... 애플은 8비트 시절 향수가 그리운건가? 제품을 만드는 방향도 해커에게 흥미거리를 던져주는 방향인 것 같다. 아마도 애플이 이렇게 배포본과 개발용 버전을 구별하는 건 고급기술(?)은 가리고 이 부분을 지원하여 이익을 추구하거나 자사 제품이 보안성이 뛰어나다는 헛소리를 받쳐주는 비지니스 전략이 아닌가 생각한다. 어짜피 제품 사용자가 많아지면 빈틈이 많아지고 보안성이 떨어지는 건 당연한다. 그 만큼 안 쓰니까 보안성이 좋다고 하는 것 뿐이다. [본문으로]
- http://www.cocoachina.com/bbs/read.php?tid=7130&page=2#63378 에서 발췌 [본문으로]
- Android 소스 [본문으로]
- http://www.androidpub.com/26015 [본문으로]
- http://social.msdn.microsoft.com/forums/en-US/vssmartdevicesnative/thread/4ad0b002-330a-47f4-b98f-12a891b9d4b1 [본문으로]
- http://www.newlc.com/en/Retrieving-IMEI-IMSI-Network-Info.html [본문으로]
- http://www.newlc.com/en/How-to-retrive-the-IMSI-number.html [본문으로]
- http://www.newlc.com/en/topic-15159 [본문으로]
댓글