Windows 에서 포트 포워딩 기능 사용하기.

VMWare등을 사용하는데, Bridged 모드로 설정하지 않고 (IP 여유가 없거나, 설정하기 귀찮을때..)

기본값인 NAT 모드로 설정해두고 아래 명령으로 하시면 됩니다.


CMD 창을 관리자 모드로 열고,


* Forwarding 설정


> netsh interface portproxy add v4tov4 listenport=80 listenaddress=192.168.0.100 connectport=80 connectaddress=192.168.229.100

 


* Forwarding 해제


> netsh interface portproxy delete v4tov4 listenport=80 listenaddress=192.168.0.100

 


* 설명

    - listenport: 내 PC에서 Listen할 Port

    - listenaddress: 내 PC에서 Listen할 IP

    - connectport: VM에 포워딩할 포트

    - connectaddress: VM의 IP

FindMSSQL

github에 올린 내용 정리해서 올립니다.

https://github.com/lmk/FindMSSQL



FindMSSQL

Find all MSSQL Server

같은 네트워크망에 설치된 MSSQL의 정보를 조회 합니다.

Visual Studio 2015 MFC 기반에서 코딩해 봤습니다.

Enveroment

  • Virsual Studio 2015
  • MFC (v140)

How to use

  • 함수 원형은 아래와 같습니다.
  • 3번째 파라미터를 생략하면 같은 네트워크망의 모든 서버를 검색합니다. (Broadcast)
bool GetMSSQLInfo(CString &info, int timeout_sec, CString ip = _T(""));

How to implement

  • MSSQL을 설치하면 SQL Server Browser가 함께 설치됩니다.
  • SQL Server Browser 접속해서 DBMS 정보를 가져옵니다.
    • UDP 1434 포트에 접속
    • Send 0x02
    • Receive Data


월간 newtype개발

월간 newtype개발

가수 윤종신님의 '월간 윤종신' 처럼, 매월 개인 프로젝트 하나씩을 해보려고 합니다.


MFC 프로젝트 버전 일괄 변경 툴

MFC 프로젝트가 여러개 일때, 버전 변경을 일일이 수정하는 것이 번거롭습니다.

이럴때, 버전일 일괄적으로 변경해 주는 툴입니다.


원리는, 설정 파일에 리소스파일 경로를 넣어둔 목록을 만들고

툴에서 파일 목록을 읽어서 아규먼트로 받은 버전으로 일괄 치환 합니다.

Visual Studio 2015 C# ( .NET Framework 4.5.2 ) 기반에서 코딩해 봤습니다.


자세한 내용은 github에 올린 내용으로 대체 합니다.

( https://github.com/lmk/changeVer )


changeVer

All change FileVersion/ProductVersion on MFC resource files

Enveroment

  • Virsual Studio 2015
  • C#

Help

Usage>
$ changeVer.exe -f FileVersion -p ProductVersion -c ConfigFilename
ex) $ changeVer.exe -f 2.1.6.33 -p 2.1.0.0 -c resources.txt
ex) $ changeVer.exe -s -c resources.txt

Options>
        -f[F]: After File version
        -p[P]: After Product version
        -c[C]: Config filename
        -s[S]: Show Current version

How to use

  1. Make list file
$ type resources.txt
D:\Source\server\server.rc
D:\Source\client\client.rc
D:\Source\client2\client2.rc
D:\Source\client3\client3.rc
  1. Execute tool
$ changeVer -f 2.1.6.34 -p 2.1.0.0 -c resources.txt
OK:     D:\Source\server\server.rc
OK:     D:\Source\client\client.rc
OK:     D:\Source\client2\client2.rc
OK:     D:\Source\client3\client3.rc

$ changeVer -s -c resources.txt
D:\Source\server\server.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"
D:\Source\client\client.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"
D:\Source\client2\client2.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"
D:\Source\client3\client3.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"


Simple Http Server

자세한 소스는 github을 참고하세요.

https://github.com/lmk/SimpleHttpServer


SimpleHttpServer

  • very simple http server
  • default port: 8080
  • support: linux g++ 4.9.2

How to use

  • block server
HttpServer httpServer;

httpServer.Init(5, NULL);
httpServer.Run();
  • non-block
NBHttpServer* httpServer = NBHttpServer::getInstance();
httpServer->Init(5);
httpServer->Start();
sleep(60);
httpServer->Stop();


OpenSSL 을 사용한 RSA

RSA

RSA 생성

  • PEM public 키로 RSA 생성하기
unsigned char *key = "PEM 형식의 public 키";
BIO *bio = BIO_new_mem_buf(key, -1);
RSA *rsa = PEM_read_bio_RSA_PUBKEY(bio, &rsa, NULL, NULL);
  • PEM private 키로 RSA 생성하기
unsigned char *key = "PEM 형식의 private 키";
BIO *bio = BIO_new_mem_buf(key, -1);
RSA *rsa = PEM_read_bio_RSAPrivateKey(bio, &rsa, NULL, NULL);
  • RSA 생성
int bits = 2048;

BIGNUM *bn = BN_new();

if ( BN_set_word(bn, RSA_F4) != 1 ) throw "BN_set_word fail";

RSA *rsa = RSA_new();

if ( RSA_generate_key_ex(rsa, bits, bn, NULL) != 1 ) throw "RSA_generate_key_ex fail";
  • 인증서(.cer) 파일로 RSA 생성하기
char path[256] = ".cer 경로";

FILE* fp = fopen(path, "rb");
if(fp == NULL) throw "File open fail";

X509 *cert = d2i_X509_fp(fp, NULL);
if(cert == NULL) throw "X509 parsing fail";

EVP_PKEY *pkey = X509_get_pubkey(cert);
if (pkey == NULL) throw "public key getting fail";

int id = EVP_PKEY_id(pkey);
if ( id != EVP_PKEY_RSA ) throw "is not RAS Encryption file";

RSA *rsa = EVP_PKEY_get1_RSA(pkey);
if ( rsa == NULL ) throw "EVP_PKEY_get1_RSA fail";
  • PCKS12(.p12) 파일로 RSA 생성하기
char path[256] = ".p12 경로";
char passwd[128] = ".p12 파일 암호"

FILE* fp = fopen(path, "rb");
if(fp == NULL) throw "File open fail";

PKCS12 *pkcs12 = d2i_PKCS12_fp(fp, NULL);
if(pkcs12 == NULL) throw "PKCS12 load fail";

EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
int result = PKCS12_parse(pkcs12, passwd, &pkey, &cert, NULL);
if ( result != 1 ) throw "PKCS12 parsing fail";

RSA *rsa = EVP_PKEY_get1_RSA(pkey);
    if ( rsa == NULL ) throw "EVP_PKEY_get1_RSA fail";
  • DER 파일 읽기
int size = 0;
FILE* fp = NULL;

try {
    fp = fopen(filename, "rb");
    if ( fp == NULL ) throw L"File open error";

    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    if ( size <= 0 ) throw L"File size is zero";

    fseek(fp, 0, SEEK_SET);

    if ( *out_len < size ) throw L"Out of memeory";

    if ( fread(out, sizeof(unsigned char), size, fp) != size ) throw L"File read error";

    *out_len = size;
}
catch(TCHAR *msg)    {
    MessageBox(msg);
    isSuccess = false;
}

if ( fp ) fclose(fp);
  • DER 형식의 private key로 RSA 생성

    • DER 형식의 private key 생성 명령
    openssl rsa -inform PEM -outform DER -in privatekey.pem -out privatekey.der
    • RSA 생성 코드
    const unsigned char *key = { /*DER 형식의 private*/ };
    const int key_len = 1192; // key의 길이
    
    EVP_PKEY *pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &key, key_len);
    if ( pkey == NULL ) throw "RSA private Key read fail";
    
    RSA *rsa = EVP_PKEY_get1_RSA(pkey);
    if ( rsa == NULL ) throw "EVP_PKEY_get1_RSA fail";
  • DER 형식의 public key로 RSA 생성(294byte)

    • DER 형식의 public key 생성 명령
    openssl rsa -pubin -in publickey.pem -inform PEM -pubout -out publickey.der -outform DER
    • RSA 생성 코드
    RSA *rsa = d2i_RSA_PUBKEY(NULL, &key, key_len);
  • DER 형식의 public key로 RSA 생성(993byte)

    • DER 형식의 public key 생성 명령
    openssl x509 -in ca.crt -pubkey -out ca_publickey.der -outform DER
    • RSA 생성 코드
    X509 *cert = d2i_X509(NULL, &key, key_len);
    if ( cert == NULL ) throw "RSA public key read fail";
    
    EVP_PKEY *pkey = X509_get_pubkey(cert);
    if (pkey == NULL) throw "public key getting fail";
    
    int id = EVP_PKEY_id(pkey);
    if ( id != EVP_PKEY_RSA ) throw "is not RAS Encryption file";
    
    RSA *rsa = EVP_PKEY_get1_RSA(pkey);
  • 순수한 public key(256byte) 로 RSA 생성하기

/**
 * @brief ASN1 포로트콜용 길이 문자열을 HEX STRING으로 만든다.
 * @param [in] size byte data 길이
 * @param [out] out HEX STRING
 * @return out
 */
inline char *ASN1_MAKE_HEX_LENGTH(int size, char *out)
{
    if( size < 0x81 ) sprintf(out, "%02X", size);
    else if ( size == 0x81 ) sprintf(out, "81%02X", size);
    else if ( size > 0x81 ) sprintf(out, "82%04X", size);

    return out;
}


const unsigned char *key = "HEX String의 256byte public 키";

char hex[540+1] = "";
unsigned char raw_buf[4096], *p;
int raw_size = 0;
char seq_length[6+1]="", int_length[6+1]="";
int seq_size_of_byte, int_size_of_byte = (strlen((const char*)key) +2) / 2; // include first "00"

ASN1_MAKE_HEX_LENGTH(int_size_of_byte, int_length);

seq_size_of_byte = 1 + (strlen(int_length)/2) + int_size_of_byte    // public key block
                    + 5;                                            // exponent block

ASN1_MAKE_HEX_LENGTH(seq_size_of_byte, seq_length);

sprintf(hex, "30%s02%s00%s0203010001", seq_length, int_length, key);

// public key size를 256byte로 고정하는 경우
//sprintf(hex, "3082010A0282010100%s0203010001", key);

p = raw_buf;

hex2binary(raw_buf, &raw_size, hex);

RSA *rsa = d2i_RSAPublicKey(NULL, (const unsigned char**)&p, raw_size);
if ( rsa == NULL ) throw "EVP_PKEY_get1_RSA fail";

RSA 에서 추출

  • RSA에서 PEM public 키 추출
BIO *bio = BIO_new(BIO_s_mem());

// -----BEGIN RSA PUBLIC KEY----- 이런 해더로 생성됨
//if ( PEM_write_bio_RSAPublicKey(bio, rsa) != 1 )  throw "PEM_write_bio_RSAPublicKey fail";

// -----BEGIN PUBLIC KEY----- 이런 해더로 생성됨
if ( PEM_write_bio_RSA_PUBKEY(bio, rsa) != 1 )  throw "PEM_write_bio_RSAPublicKey fail";

int size = BIO_get_mem_data(bio, &p);
if ( size <= 0 ) throw "Public size is zero";
size++; // include null

char *buffer = new char[size];

int read_size = BIO_read(bio, buffer, size-1);
if ( read_size != size-1 ) throw "Public read fail";
  • RSA에서 PEM private 키 추출
BIO *bio = BIO_new(BIO_s_mem());

if ( PEM_write_bio_RSAPrivateKey(bio, _rsa, NULL, NULL, 0, NULL, NULL) != 1) throw "PEM_write_bio_RSAPrivateKey fail";

int size = BIO_get_mem_data(bio, &p);
if ( size <= 0 ) throw "Private size is zero";
size++; // include null

char *buffer = new char[size];

int read_size = BIO_read(bio, buffer, size-1);
if ( read_size != size-1 ) throw "Private read fail";
  • RSA에서 순수한 Public 키(256byte) 추출 하기(DER 파싱)
/**
  @breif der를 한번 파싱한다.
  @param [in] in 파싱 시작위치
  @param [out] tag 태그 type
  @param [out] length data length
  @param [out] data data 시작위치
  @return true 성공
*/
bool parsingDer(const unsigned char *in, unsigned char *tag, int *length, unsigned char **data)
{
    int offset = 0;
    *tag = in[0];

    if ( in[++offset] == 0x82 ) {
        *length = (int)(in[offset+1] << 8 | in[offset+2]);
        offset += 2;
    } else {
        *length = (int)(in[++offset]);
    }

    *data = ( unsigned char *)&in[++offset];

    return true;
}

unsigned char out[256];
unsigned char tag, *buf, *start_pos, *data_pos;

int data_len = i2d_RSAPublicKey( rsa, &buf );
if ( data_len < 0 ) throw "Fail get public key from rsa";

start_pos = buf;

parsingDer(start_pos, &tag, &data_len, &data_pos);
if ( tag != 0x30 ) throw "Fail parsing at SEQUENCE";

start_pos = data_pos;
parsingDer(start_pos, &tag, &data_len, &data_pos);
if ( tag != V_ASN1_INTEGER ) throw "Fail parsing at INTEGER";

memcpy(out, &(data_pos[1]), data_len-1);
  • RSA에서 시작일 추출
ASN1_TIME* atime_before = NULL;

atime_before = X509_get_notBefore(_cert);
if ( atime_before == NULL ) throw "X509_get_notBefore fail";
  • RSA에서 만료일 추출
ASN1_TIME* atime_after = NULL;

atime_after = X509_get_notAfter(_cert);

if ( atime_after == NULL ) throw "X509_get_notAfter fail";

ASN1_TIME을 문자열로 변환

char* ASN1_TIME_to_string(const ASN1_TIME* time, char out[DT_STRING_LENGTH])
{
    struct tm t;
    const char* str = (const char*) time->data;
    size_t i = 0;

    memset(&t, 0, sizeof(t));

    if (time->type == V_ASN1_UTCTIME) {/* two digit year */
        t.tm_year = (str[i++] - '0') * 10;
        t.tm_year += (str[i++] - '0');
        if (t.tm_year < 70)
            t.tm_year += 100;
    } else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
        t.tm_year = (str[i++] - '0') * 1000;
        t.tm_year+= (str[i++] - '0') * 100;
        t.tm_year+= (str[i++] - '0') * 10;
        t.tm_year+= (str[i++] - '0');
        t.tm_year -= 1900;
    }
    t.tm_mon  = (str[i++] - '0') * 10;
    t.tm_mon += (str[i++] - '0') - 1; // -1 since January is 0 not 1.
    t.tm_mday = (str[i++] - '0') * 10;
    t.tm_mday+= (str[i++] - '0');
    t.tm_hour = (str[i++] - '0') * 10;
    t.tm_hour+= (str[i++] - '0');
    t.tm_min  = (str[i++] - '0') * 10;
    t.tm_min += (str[i++] - '0');
    t.tm_sec  = (str[i++] - '0') * 10;
    t.tm_sec += (str[i++] - '0');

    /* Note: we did not adjust the time based on time zone information */
    time_t tt = mktime(&t);
    //strftime(out, DT_STRING_LENGTH, "%Y-%m-%d %H:%M:%S", localtime(&tt));
    strftime(out, DT_STRING_LENGTH, "%Y%m%d", localtime(&tt));

    return out;
}

암호화

  • public키로 생성한 RSA로 암호화
RSA_public_encrypt(flen, from, to, rsa, padding);
  • private키로 생성한 RSA로 암호화
RSA_private_encrypt(flen, from, to, rsa, padding);

복호화

  • public키로 생성한 RSA로 복호화
RSA_public_decrypt(flen, from, to, rsa, padding);
  • private키로 생성한 RSA로 복호화
RSA_private_decrypt(flen, from, to, rsa, padding);

base64

  • 인코딩
int raw_size = 256;
unsigned char raw[256] ={/* RAWDATA */};

int bas64_size = 1.5 * raw_size;
char *out = new char[bas64_size];

bas64_size = EVP_EncodeBlock((unsigned char*)out, (const unsigned char*)raw, raw_size);
out[bas64_size++] = 0;

기타 함수

  • hex string을 binary(RAWDATA)로 만들기
void hex2binary(unsigned char *dst, int *dst_len, const char *src)
{
    int src_len = strlen(src);
    char *end =0;
    char buf[3] = {0,};
    int i=0;

    for(i=0; i<src_len; i++) {
        strncpy(buf, &src[i*2], 2);
        dst[i] = (char)strtol(buf, &end, 16);
    }

    *dst_len = i/2;

    return;
}


Code Finder

Code Finder

개요

설치

git clone https://github.com/lmk/code_finder.git
cd code_finder
npm install
npm run build

실행

npm run start

내 해더 파일을 사용하는 방법

  • /build/config.json 파일을 수정합니다.
{
  "list": [
    {
      "id" : "5RAcQeaaF4XYtVaAwg4Gtqf2C4xbpU7v",
      "title": "Sample Project",
      "files": [
        "sample-project/error.h",
        "sample-project/type.h"
      ],
      "option": {
        "checked": true
      }
    },
    {
      "id" : "FCg4h39RBah2hL99n6w4MYmLsd0nTslf",
      "title": "openssl",
      "files": [
        "openssl/err.h"
      ]
    }
  ]
}
  • "id": 고유 ID 아무 값이나 상관없습니다. 여기서는 32자리 해시 코드를 사용했습니다.
  • "title": 프로젝트 제목
  • "files": 파싱할 해더파일 목록

Restriction

  • Edge 지원합니다.
  • Chrome 지원합니다.
  • Internet Explorer 지원합니다. (isomorphic-fetch를 사용했습니다.)
  • node v6.0 이상 설치되어 있어야 합니다.
  • python 2.7 설치되어 있어야 합니다.



C 상수 파서

아주 오래전에 만들어 놓은 것을 github에 올리면서 readme 파일을 작성했습니다.

https://github.com/lmk/c_define_parser


개요

c 헤더 파일을 파싱해서 '#define', 'enum' 구문으로 정의된 값, 상수명을 검색합니다.

사용법

error.h 파일의 내용이 아래와 같을 때

#define ERROR_NONE 0x00
#define ERROR_LOGIN 0x01
#define ERROR_FILE 0x02
#define ERROR_DEVICE 0x03
$ find_const.py 1
1 is ERROR_LOGIN

$ find_const.py 0x03
0x03 is ERROR_DEVICE

구현 로직

  1. 모든 파일을 머지한다.
  2. 주석을 제거한다.
  3. enum 구문을 파싱해서 dic_command에 저장한다.
  4. define 구문을 파싱해서 dic_command에 저장한다.
  5. dic_command에 저장된 상수 값을 찾아서 치환 한다.
  6. dic_command의 key, value 값을 바꿔서 dic_command_r에 저장한다.
  7. 값으로 상수를 찾는다.

선행 작업

find_const.py 파일 9번째 줄에 FILE_PATH 배열에 파싱할 헤더파일 목록을 추가합니다.

python에서 ctype을 이용한 c library 연동 예제

python의 ctype 모듈을 이용해서 visual c++로 작성한 동적 dll을 읽는 sample 코드 입니다.

소스 코드 설명

  • /vc/test_dll: 동적 dll 코드 입니다.

    • SET_VALUE: int 형 값을 저장합니다.
    • GET_VALUE: 저장한 int형 값을 읽어 옵니다.
    • SET_CALLBACK: 함수 포인터를 저장합니다. python으로 코딩한 함수를 넘길 것 입니다.
    • RUN_CALLBACK: 저장한 함수 포인터에 정수형 값을 넘겨 실행 합니다. 실행하면 python함수가 실행될 것 입니다.
    • SET_CLASS_CALLBACK: c++ class를 사용해서 함수 포인터를 저정합니다.
    • RUN_CLASS_CALLBACK: c++ class를 사용해서 저장한 함수 포인터를 실행합니다.
    • SET_THREAD_CALLBACK: 함수 포인터를 저장하고, thread 에서 1초 후 함수를 실행합니다.
    • INIT_CLASS: c++ class를 동적으로 생성합니다.
    • SET_D_CLASS_CALLBACK: 동적으로 생성한 class를 사용해서 함수 포인터를 저장합니다.
    • RUN_D_CLASS_CALLBACK: 동적으로 생성한 class를 사용해서 함수 포인터를 실행합니다.
  • /vc/call_dll: test_dll을 LoadLibrary() 해서 순서대로 테스트 하는 c++ 코드 입니다.(test_dll.dll 파일의 경로는 맞게 수정해야 동작합니다)

  • /python: 동적 dll 읽어 순서대로 테스트 하는 python 코드 입니다.(test_dll.dll 파일의 경로는 맞게 수정해야 동작합니다)



OCILib를 사용하는 경우 필수 파일

* instantclient-basic-windows.x64-12.1.0.2.0.zip
    - oci.dll
    - oraociei12.dll
    - oraons.dll

* msvcr100.dll

* ocilibw.dll