'분류 전체보기'에 해당되는 글 66건

  1. 2015.04.24 ㅋㅋ
  2. 2015.04.10 Heroes of the storm 무료 베타키
  3. 2015.04.08 [Unity3D] CompareBaseObjectsInternal can only be called from the main thread
  4. 2015.04.08 UserPref 암호화
  5. 2015.03.19 easing demo
  6. 2012.05.02 내가 추구하는 회사(SBS 현장21 - 천국보다 멋진)
  7. 2012.04.25 뻥쟁이들
  8. 2012.04.12 19대총선
  9. 2011.06.09 6월 8일 한화, LG 보크 오심
  10. 2011.05.18 IWebBrowser2 Scrollbar 숨기기 1

ㅋㅋ

|

And

Heroes of the storm 무료 베타키

|






베타키 4개를 받았다.

누굴 줘야하나...


And

[Unity3D] CompareBaseObjectsInternal can only be called from the main thread

|

출처 : http://lazli.tistory.com/entry/Unity3D-CompareBaseObjectsInternal-can-only-be-called-from-the-main-thread

And

UserPref 암호화

|

출처 : http://ikpil.com/1342



포스팅 목적

  • 기록하기 위해
  • 피드백을 받기 위해. 버그 있을 때, 수정 할 수 있습니다. 링크도 가져 가세요.
  • 공유 - 불펌 개념이 없으니, 마음데로 사용하세요.

개발 이유

게임 정보를 서버에 다 저장해서 쓰려고 했는데, 회의 중 인터넷이 끊어져도 게임하는데 지장이 없어야 한다는 결론이 내려졌습니다. 그래서 로컬에 데이터를 저장해야 하는데, 유니티가 기본 제공하는 PlayerPrefs 은 손 쉽게 변조가 가능하기 때문에, 변조를 못하게 막아야 할 필요가 있었습니다.

그래서 유니티 보안 문서들을 보았고, 그 중에 PlayerPrefs 먼저 할 필요가 있어서, 기존에 다른 사람이 만든 코드를 찾아 보았습니다. 하지만 키를 숨기는 개념이나 value 암호화 개념을 사용하는건 찾지 못했습니다. 그래서 직접 개발(짜집기)을 하게 되었습니다.

다음에는 모듈(DLL) 암호화를 할 필요가 있습니다.

링크 1 : http://unitystudy.net/bbs/board.php?bo_table=newwriting&wr_id=355
링크 2 : ?http://www.slideshare.net/williamyang3910/unitekorea2013-protecting-your-android-content-21713675?

설명

Unity PlayerPrefs 특성상 key-value 형태로 로컬에 데이터를 저장하는데, 암호화가 되어 있지 않습니다. 그래서 다음과 같은 절차를 거쳐 PlayerPrefs 를 사용합니다.

저장 할 때

  1. 저장 할 Key 의 해쉬값을 얻고, 그 해쉬를 hideKey 로 사용합니다. 왜냐하면, 해커가 Key 를 보고, 용도를 파악 할 수 있기 때문입니다. 추가로 salt 가 있는 이유는 공개 해쉬 알고리즘을 사용하기 때문에, 무작위 대입으로 키를 손쉽게 알 수 있기 때문입니다.
    예) hideKey = Hash(Key + salt)

  2. 저장해야 할 Value 의 해쉬값을 얻고, Value + Hash(Value) 로 만든 뒤 암호화 합니다.
    Value 의 Hash 를 얻는 이유는 복호화 후 Value 가 변조되었는지 확인이 필요하기 때문이다.
    예) encryptValue = Encrypt(Value + Hash(Value)) 

3. 이렇게 얻어진 hideKey - encryptValue 를 PlayerPrefs 으로 저장합니다.

불러 올 때

  1. hideKey 를 똑같이 만듭니다.
  2. hideKey 로 암호화된 값을 얻고, 복호화 후 원본 saved Value + saved Hash(Value) 를 구합니다.
  3. saved Value, saved Hash(Value) 로 분리 한 뒤, 서로 비교하여 변했는지 검사합니다. 
    예) Hash(saved Value) == saved Hash(Value)

코드 : SecurityPlayerPrefs , 갱신 : 2015.03.03 23:16

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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
using UnityEngine;
using System.Security.Cryptography;
using System.Text;
using System.IO;
  
public class SecurityPlayerPrefs
{
 
    private static string _saltForKey;
 
    private static byte[] _keys;
    private static byte[] _iv;
    private static int keySize = 256;
    private static int blockSize = 128;
    private static int _hashLen = 32;
 
    static SecurityPlayerPrefs()
    {
        // 8 바이트로 하고, 변경해서 쓸것
        byte[] saltBytes = new byte[] { 25, 36, 77, 51, 43, 14, 75, 93 };
 
        // 길이 상관 없고, 키를 만들기 위한 용도로 씀
        string randomSeedForKey = "5b6fcb4aaa0a42acae649eba45a506ec";
 
        // 길이 상관 없고, aes에 쓸 key 와 iv 를 만들 용도
        string randomSeedForValue = "2e327725789841b5bb5c706d6b2ad897";
 
        {
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(randomSeedForKey, saltBytes, 1000);
            _saltForKey = System.Convert.ToBase64String(key.GetBytes(blockSize / 8));
        }
 
        {
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(randomSeedForValue, saltBytes, 1000);
            _keys = key.GetBytes(keySize / 8);
            _iv = key.GetBytes(blockSize / 8);
        }
    }
 
    public static string MakeHash(string original)
    {
        using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(original);
            byte[] hashBytes = md5.ComputeHash(bytes);
 
            string hashToString = "";
            for (int i = 0; i < hashBytes.Length; ++i)
                hashToString += hashBytes[i].ToString("x2");
 
            return hashToString;
        }
    }
 
    public static byte[] Encrypt(byte[] bytesToBeEncrypted)
    {
        using (RijndaelManaged aes = new RijndaelManaged())
        {
            aes.KeySize = keySize;
            aes.BlockSize = blockSize;
 
            aes.Key = _keys;
            aes.IV = _iv;
 
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
 
            using (ICryptoTransform ct = aes.CreateEncryptor())
            {
                return ct.TransformFinalBlock(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
            }
        }
    }
 
    public static byte[] Decrypt(byte[] bytesToBeDecrypted)
    {
        using (RijndaelManaged aes = new RijndaelManaged())
        {
            aes.KeySize = keySize;
            aes.BlockSize = blockSize;
 
            aes.Key = _keys;
            aes.IV = _iv;
 
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
 
            using (ICryptoTransform ct = aes.CreateDecryptor())
            {
                return ct.TransformFinalBlock(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
            }
        }
    }
 
    public static string Encrypt(string input)
    {
        byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
        byte[] bytesEncrypted = Encrypt(bytesToBeEncrypted);
 
        return System.Convert.ToBase64String(bytesEncrypted);
    }
 
    public static string Decrypt(string input)
    {
        byte[] bytesToBeDecrypted = System.Convert.FromBase64String(input);
        byte[] bytesDecrypted = Decrypt(bytesToBeDecrypted);
 
        return Encoding.UTF8.GetString(bytesDecrypted);
    }
 
    private static void SetSecurityValue(string key, string value)
    {
        string hideKey = MakeHash(key + _saltForKey);
        string encryptValue = Encrypt(value + MakeHash(value));
 
        PlayerPrefs.SetString(hideKey, encryptValue);
    }
 
    private static string GetSecurityValue(string key)
    {
        string hideKey = MakeHash(key + _saltForKey);
 
        string encryptValue = PlayerPrefs.GetString(hideKey);
        if (true == string.IsNullOrEmpty(encryptValue))
            return string.Empty;
 
        string valueAndHash = Decrypt(encryptValue);
        if (_hashLen > valueAndHash.Length)
            return string.Empty;
 
        string savedValue = valueAndHash.Substring(0, valueAndHash.Length - _hashLen);
        string savedHash = valueAndHash.Substring(valueAndHash.Length - _hashLen);
 
        if (MakeHash(savedValue) != savedHash)
            return string.Empty;
 
        return savedValue;
    }
 
    public static void DeleteKey(string key)
    {
        PlayerPrefs.DeleteKey(MakeHash(key + _saltForKey));
    }
 
    public static void DeleteAll()
    {
        PlayerPrefs.DeleteAll();
    }
 
    public static void Save()
    {
        PlayerPrefs.Save();
    }
 
    public static void SetInt(string key, int value)
    {
        SetSecurityValue(key, value.ToString());
    }
 
    public static void SetLong(string key, long value)
    {
        SetSecurityValue(key, value.ToString());
    }
 
    public static void SetFloat(string key, float value)
    {
        SetSecurityValue(key, value.ToString());
    }
 
    public static void SetString(string key, string value)
    {
        SetSecurityValue(key, value);
    }
 
    public static int GetInt(string key, int defaultValue)
    {
        string originalValue = GetSecurityValue(key);
        if (true == string.IsNullOrEmpty(originalValue))
            return defaultValue;
 
        int result = defaultValue;
        if (false == int.TryParse(originalValue, out result))
            return defaultValue;
 
        return result;
    }
 
    public static long GetLong(string key, long defaultValue)
    {
        string originalValue = GetSecurityValue(key);
        if (true == string.IsNullOrEmpty(originalValue))
            return defaultValue;
 
        long result = defaultValue;
        if (false == long.TryParse(originalValue, out result))
            return defaultValue;
 
        return result;
    }
 
    public static float GetFloat(string key, float defaultValue)
    {
        string originalValue = GetSecurityValue(key);
        if (true == string.IsNullOrEmpty(originalValue))
            return defaultValue;
 
        float result = defaultValue;
        if (false == float.TryParse(originalValue, out result))
            return defaultValue;
 
        return result;
    }
 
    public static string GetString(string key, string defaultValue)
    {
        string originalValue = GetSecurityValue(key);
        if (true == string.IsNullOrEmpty(originalValue))
            return defaultValue;
 
        return originalValue;
    }
}

결과



여담

  • UnityEditor 에서 PlayerPrefs 을 수정/보기 가능하면, 개발이 좀 더 편해집니다.
    : 누군가 만들면, 알려 주세요.

  • 믿을건 그나마 서버 밖에 없습니다.

:wq!



And

easing demo

|


And

내가 추구하는 회사(SBS 현장21 - 천국보다 멋진)

|

 내가 앞으로 추구하고 만들어가고 싶은 회사의 모습이다.

 

1. 천국 같은 직장을 목표로 끊임없이 노력한다.

 

 

 

 

2. 천국 같은 직장을 위해 끊임 없이 노력하면 "올만한 가치가 있는 회사"로 인재는 모인다.

 

 

 

 

 

 

 

3. 직원의 복지와 자기 계발은 회사를 위한 투자이다.

 

 

 

 

 

 

 

 

4. 회사의 기본 원칙 "노동자로서의 도구가 아닌 창조의 가치를 지닌 한사람의 인간"

 

 

 

 

 

5. 회사의 직원의 원칙 "신뢰와 배려"

 

 

 

 

 

 

 

6. 야근 지양 - 필수가 아닌 선택

 

 

 

 

 

 

 

 

7. 직원을 만족시키기 위한 것은 "비용"이 아닌 "투자"

 

 

 

 

 - 정리

1. 천국 같은 직장을 목표로 끊임없이 노력한다.

2. 천국 같은 직장을 위해 끊임 없이 노력하면 "올만한 가치가 있는 회사"로 인재는 모인다.

3. 직원의 복지와 자기 계발은 회사를 위한 투자이다.

4. 회사의 기본 원칙 "노동자로서의 도구가 아닌 창조의 가치를 지닌 한사람의 인간"

5. 회사의 직원의 원칙 "신뢰와 배려"

6. 야근 지양 - 필수가 아닌 선택

7. 직원을 만족시키기 위한 것은 "비용"이 아닌 "투자"

 

 나의 게임 회사에 대한 신념 : 회사는 사람을 키우고 그 사람이 게임을 만든다.

 

회사가 게임을 만들기 위해 사람을 고용하여 일을 시키는 생각은 버려야 한다는 것이다.

 

잊지 말자

And

뻥쟁이들

|

 

 

정부가 지난 2008년 5월 8일자 신문에 실었던 추가 광우병 발생시 대국민 약속을 밝힌 광고.

 

 

조선일보 2008년 5월 8일자 1면 머리기사

 

 

그러나

 

...

그러나 우리 정부는 미국에서 제공한 정보가 제한적이라고 판단하면서도 이로 인한 통상마찰을 예방하기 위한 우선적인 조치가 정보 요구라고 설명했다. 광우병이 발생했는데도 우선 수입중단 조치도 하지 않은채 더 물어보고 판단하겠다는 것이다.

http://www.mediatoday.co.kr/news/articleView.html?idxno=102039 (내용중 발췌)

...

 

 

 

이런 개구리 뻥쟁이들

And

19대총선

|

 

 

19대 총선... 이게 우리나라의 현주소다.

 

위 아래로는 남과 북으로 나라가 나뉘고

 

좌우로는 여야가 나뉜다.

 

난 우파 좌파로 나누는 것이 싫지만 굳이 따지자면 좌파성향에 조금더 가깝다.

 

그렇다고 민주당을 좋아하진 않는다. 새누리당은 더더욱...

 

난 소수정당을 응원하고 지지한다. 소수 정당에게 힘을 실어줘야 거대 정당들이(여야를 떠나) 정신을 차릴 것이다.

 

대기업만 살아남는 사회가 아닌 중소기업이 커지는 사회가 좋다.

 

어느 정치철학자가 그랬던가 여당이든 야당이든 어느 한쪽도 사라져서는 안된다는 것... 공존해야한다는 것...

 

그래 공존이다. 한쪽으로 치우치지 않는...

 

그러나 위의 대한민국 지도의 동쪽에서는 공존은 필요없어 보인다. 전라 남북도도 마찬가지..

 

안타까운 현실이다.

 

타이틀만, 이름만, 외모만 보지 말고 속을 봐야 후회하지 않을 것이다.

 

빨갱이소리 그만하고, 수구꼴통 소리 그만하고 서로 힘을 합쳐야 하지 않을까?

 

한숨만 나온다.

 

소수정당 화이팅~ 중소기업 화이팅~

And

6월 8일 한화, LG 보크 오심

|

KBO는 심판의 보크 오심을 인정했다.

허나 나는 정원석 선수의 홈스틸 오심도 문제라고 생각된다.

그 이유는 아래 사진에 있다.

- 위 사진은 http://www.koreabaseball.com/News/PhotoView.aspx?BD_SE=3444 KBO 홈페이지에서 캡쳐한 사진입니다.

KBO 홈페이지에서 포토 게시판에 올라온 사진과 글이다.
정원석 선수가 분명 홈스틸을 시도하여 홈에 발이 거의 다달았다.
하지만 포수의 글러버는 보이지 않는다.
포수는 이 상황에서 주자가 달려오는 반대편에서 투수로부터 홈송구를 받았다. (이건 보크 오심이죠)
그럼 위 사진 상황에서 포수가 주자에게 태그를 하기위해 달려오고 있을 것이다.
10cm도 안되는 발과 사진에서 벗어나서 보이지도 않는 포수의 글러브 중에 누가 더 빨랐겠는가?

보크를 오심 인정했다 하나 나는 이 홈스틸 장면도 오심이라 생각된다.
이 홈스틸 오심도 KBO에서 사과를 해야할 것이라 생각이 든다.
And

IWebBrowser2 Scrollbar 숨기기

|


이전 글에서는 IWebBrowser2 로 IE Control을 삽입하는 방법을 썼다.

이번엔 그 IE Control 에서 Scrollbar 를 숨기는 방법이다. (이거 알아 내느냐고 하루를 소비했다. ㅠㅜ)

세가지 방법이 있다.

1. IWebBrowser2로 IDispatch를 얻어서 IHTMLDocument2 --> IHTMLElement --> IHTMLBodyElement 까지 얻는다.
그리고 IHTMLBodyElement 의 put_scroll(CComBSTR(L"no")) 해주면 된다.
이 방법은 인터넷에 찾으면 많이 나온다.
주의 할 사항은 간혹 html 인지 asp 인지 어느 url 에서는 읽지 못하고 IHTMLElement 읽어 오는데 실패한다.
그래서 이 방법은 추천하지 않는다.

2. 두번째 방법은 IDocHostUIHandler::GetHostInfo 의 플래그 값에 DOCHOSTUIFLAG_SCROLL_NO 를 넣어 주면 된다.
하지만 MFC 에서는 Custsite.cpp, Custsite.h, Idispimp.cpp, Idispimp.h 를 추가하여 간단하게 처리 가능하나 ATL 용으로는 약간 더 복잡하다.
IDocHostUIHandlerDispatch 를 이용하여 IAxWinHostWindow::SetExternalUIHandler(IDocHostUIHandlerDispatch *pDisp)에 플래그를 세팅하여 
넣어 줘야 한다.

3. 세번째는 Navigate(...) 하기전에 IAxWinAmbientDispatch 의 플래그에 DOCHOSTUIFLAG_SCROLL_NO 를 넣어 주기만 하면 된다.
CoCreateInstance( ,,, IWebBrowser2 객체 ) 이후
AtlAxAttachControl( IWebBrowser2 객체, Window Handle, &IUnknown객체 );   // - atl 컨트롤 연결

....

CComPtr<IAxWinAmbientDispatch> axWinDisp;
if( SUCCEEDED( IUnknown객체->QueryInterface( &axWinDisp ) ) )
{
        axWinDisp->put_DocHostFlags( DOCHOSTUIFLAG_SCROLL_NO );   // - HostFlag 설정
        // DOCHOSTUIFLAG_NO3DOUTERBORDER | DOCHOSTUIFLAG_NO3DBORDER 도 넣으면 테두리가 사라진다.
}
여기까지 하면 Visual Studio 6.0 쓰시는 분들은 잘 될 것입니다. 허나 그 이상 버전에서는 AtlAxAttachControl 함수 안에서 실행중 에러가 난다.

... 
HRESULT hRes = E_OUTOFMEMORY;
  T1* p = NULL;
#pragma warning(push)
#pragma warning(disable: 6014)
  /* prefast noise VSW 489981 */
  ATLTRY(p = new T1(pv))    // <<<<========== 여기에서 크래쉬
#pragma warning(pop)
  if (p != NULL)
...
구글링 끝에 해외 개발자 커뮤니티 사이트에서 찾아냈다. (야호~)

작업 중인 cpp에 (웹 컨트롤 class가 있는...) 제일 상단에 아래 코드를 넣어주면 된다.

CComModule _Module;
extern __declspec(selectany) CAtlModule* _pAtlModule = &_Module;

잘 돌아 갈 것이다.

두번째, 세번째 방법의 더 자세한 내용은 http://ancdesign.tistory.com/17 참조.
마지막 크래쉬 관련 내용은 http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/6de21bab-7786-497f-99ba-c40de8ff1320/ 마지막 코맨트 참조.

And
prev | 1 | 2 | 3 | 4 | ··· | 7 | next