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


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;
}


Visual Leak Detector 를 사용하여 Leak을 감지하는 방법.

Visual Leak Detector 를 사용하여 Leak을 감지하는 방법.

사용조건

  • Visual Studio 2008/2010/2012/2013/2015

사용방법 (VS2010 기준으로 설명합니다.)

  • Visual Sutdio 설정을 합니다.
    • VS를 실행하고, 적용할 솔루션을 엽니다.
    • VS를 실행해서 보기 > 다른 창 > Property Manager 창을 엽니다.
    • Property Manager 창에서 아무 프로젝트에서든 선택하고 적용하고자 하는 구성에서 "Microsfot.Cpp.x64.user"를 더블클릭 합니다.
    • 속성 페이지가 열리면, 왼쪽 메뉴 트리에서 VC++ Directorys > Include Directories에 VLD의 include 경로를 추가합니다.
      • 저는 "C:\Program Files (x86)\Visual Leak Detector\include" 여기에 설치되어 있네요.
    • 같은 창의 Libaray Directories에 VLD의 lib 경로를 추가합니다.
      • 저는 64 비트라서 "C:\Program Files (x86)\Visual Leak Detector\lib\Win64"를 추가 했네요.
      • 한번만 하면 솔루션 아래 여러개의 프로젝트가 있는경우 모두 반영됩니다.
  • stdafx.h 파일에 아래 코드를 추가합니다. 
    #include <vld.h>
    
    • 공식 Document에는 stdafx.h 호출후에 넣으라고 하는데, stdafx.h 파일에 넣어도 잘 돌아갑니다.
    • 공통되는 헤더 파일이 있으면 거기에 추가하면 됩니다.
  • 이제 VS에서 F7로 컴파일하고, F5로 실행합니다.
  • 프로그램이 정상 종료되면, 출력 > 디버그 창에 Leak 정보가 출력 됩니다.
    • VS의 디버그 중지로 하면 안됩니다.

참고

  • 직접 실행하는 경우.
    • 컴파일된 실행 파일을 VS에서 실행하지 않고 탐색기(또는 CMD)에서 직접 실행 하는 경우 아래 파일이 실행 파일과 같은 경로에 있어야 합니다.
      • dbghelp.dll
      • Microsoft.DTfW.DHL.manifest
      • vld_x64.dll
    • 해당 파일은 64비트의 경우 C:\Program Files (x86)\Visual Leak Detector\bin\Win64 에 있습니다.


debian 계열 linux에서 core 파일 생성 방법

1. core 파일을 최소 크기를 설정 한다. 

 $ ulimit -c unlimited 
 $ ulimit -a 

 2. core 파일의 네이밍 룰을 지정한다. 

 $ echo "/etc/core" > /proc/sys/kernel/core_pattern 

> login 시 마다 동일한 설정을 해야 한다. 

[ 참조 ] 
http://kthan.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4Linux-ulimit-%EC%9C%BC%EB%A1%9C-coredump-%EC%84%A4%EC%A0%95 
http://manywaypark.tistory.com/entry/core-file%EC%9D%80-%EC%96%B4%EB%94%94%EC%97%90


Serial-port 관련 자료 정리

centos 에서 Serial-Port를 이용한 파일전송 모듈을 개발하면서 정리한 자료 입니다.

 

시리얼 포트 상태 확인

  • 장치 확인

    $ cat /proc/tty/driver/serial
    serinfo:1.0 driver revision:
    0: uart:16550A port:000003F8 irq:4 tx:930342833 rx:215304 brk:145317 RTS|CTS|DTR|DSR
    1: uart:16550A port:000002F8 irq:3 tx:1090 rx:0 RTS|CTS|DTR|DSR|CD
    2: uart:unknown port:000003E8 irq:4
    3: uart:unknown port:000002E8 irq:3
  • 커널에서 시리얼 드라이버 load 되었는지 확인

    $ dmesg | grep tty

    • 정상적인 경우
      serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
      serial8250: ttyS1 at I/O 0x2f8 (irq = 3) is a 16550A
      00:09: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
    • 예외 상황
      Xen virtual console succefully installed as ttyS0
      • Xen에서 가상콘솔을 사용하는 듯.
      • 출처: https://kldp.org/node/91683
      • 해결 방안: 부팅 관리자에서 Xen이 없는 것으로 부팅
        $vi /boot/grub/grub.conf
        default = 1   <- Xen이 없는 것으로 부팅 '0' 시작 Index
  • 간략한 포트 확인

    $ setserial -a /dev/ttyS0
    /dev/ttyS0, Line 0, UART: 16550A, Port: 0x03f8, IRQ: 4
      Baud_base: 115200, close_delay: 50, divisor: 0
      closing_wait: 3000
      Flags: spd_warp skip_test

  • 조금 더 자세한 포트 확인

    $ stty -F /dev/ttyS0
    speed 115200 baud; line = 0;
    intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; start = <undef>; stop = <undef>; susp = <undef>;
    rprnt = <undef>; werase = <undef>; lnext = <undef>; flush = <undef>; min = 0; time = 100;
    -brkint -icrnl -imaxbel
    -opost -onlcr
    -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke

  • 상세한 포트 확인

    stty -a < /dev/ttyS0
    speed 115200 baud; rows 0; columns 0; line = 0;
    intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; eol = <undef>; eol2 = <undef>; swtch = <undef>;
    start = <undef>; stop = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; flush = <undef>;
    min = 0; time = 100;
    -parenb -parodd cs8 -hupcl -cstopb cread clocal crtscts -cdtrdsr
    -ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8
    -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
    -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke

시리얼 포트 설정 변경 및 복구

시리얼 포트로 접속 테스트

OSX 에서 시리얼 포트 테스트 방법

  • 시리얼 포트가 없으므로 USB2Serial 젠더나 케이블이 필요하다.
  • 터미널에서 screen 유틸 사용
  • GUI에서 SerialTools 유틸 사용(AppStore에 있음) - 연결 상태를 눈으로 확인 할 수 있어서 추천함.

시리얼 포트 통신 프로그래밍

  • 시리얼 통신 특이사항
    • BAUDRATE을 높게 잡아도 특정 수치 이후로는 속도가 같다.
      • null cable 사용시 11,531 byte per sec 정도 나옴.
    • 버퍼를 255 이상 잡으면 짤려서 전송된다.
      • 송/수신시 나눠서 전송 할 필요가 있다.
    • 전송중에 케이블을 빼도 상태를 알수가 없다.
      • 프토토콜을 정의할 필요가 있다.
    • non-canonical, x초(0.1초이상) 동안 입력이 없으면, timeout 처리 되록 하는것이 최적인듯 하다.
      newtio.c_lflag     = 0;
      newtio.c_cc[VTIME] = 100;
      newtio.c_cc[VMIN]  = 0;
    • 속도가 매우 느리므로 데이터를 압축해서 보내는 것이 좋다.
  • 문서

시리얼 포트로 터미널 접속 셋팅(서버측)

참고 문서



core 파일이 생성되는 한가지 상황

오늘 하루종일 삽질한 내용이 있어서 기록을 남긴다.

[ 상황 ]
패킷 내용이 추가되어서 기존 모듈을 수정하게 되었다.
컴파일을 잘되는데, 실행하면 계속 core 파일이 생성되면서 프로세스가 죽는다.
수정 한 부분을 전체 주석으로 막아도, Call Stack을 체크 해보면 특정 함수를 진입하는 시점에 죽어 버린다.

[ 원인 ]
원인은 패킷 사이즈가 커지면서, Stack 영역에 할당 할 수 있는 최대 메모리 사이즈를 초과한 것이다.

[ 해결 방안 ]
Stack 영역이 아닌, Heap영역에 메모리를 할당 하면 된다.

원인 밝히느라 오전 내내 삽질, 
정적 메모리 구조를 동적 구조로 바꾸느라고, 오후 내내 디버깅 및 삽질..
오늘 하루도 이렇게 날려 먹었다.

[ Code ]
-. Stack 영역에 할당 할 수 있는 최대 크기 확인 ( 단위 KB )

$ ulimit -s
 8192



-. 테스트 프로그램.

// cc -o maxsize maxsize.c
#include <stdio.h>

typedef char PACKET[5600000];

int main()
{
        PACKET pkt;
        //PACKET pkt2;  
        PACKET *pkt3;

        printf ("\n");
        printf ( "%s: %d\n", "PACKET", sizeof(PACKET) );

        memset ( &pkt, 0, sizeof(pkt) );
        //memset ( &pkt2, 0, sizeof(pkt2) );   // rise core dump  

        pkt3 = (PACKET*)malloc(sizeof(PACKET));
        if ( !pkt3 ) printf("not enough momery\n");
        else memset( pkt3, 0, sizeof(PACKET) );

        return 0;
}

실행 해 보면,  아래와 같은 결과를 얻는다.

$ maxsize
PACKET: 5600000



 하지만, 9, 16 라인의 주석을 풀고 실행해보면, 아래와 같이 core 파일이 생성된다.

$ maxsize
세그멘테이션 결함(Segmentation Fault)(코어 덤프)


[ 참고문서 ]
http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/stack_size 


 

Pro*C/C++ Precompiler Programmer's Guide


[ 원문 링크 ]

http://download.oracle.com/docs/cd/A58617_01/server.804/a58233/toc.htm

[ 다운로드 링크 ]


한글이 포함된 문자열을 특정 byte로 자르기


한글이 포함된 문자열의 경우에 한글 중간에 끊기지 않도록 left 함수를 구현 했다.
유니코드 한글을 구분하기 위해서는 메크로 함수를 수정해 주어야 한다.

#include <stdio.h>
    
#define IsHangle(c)  ((unsigned char)(c) > 0x7f)

char* left(char* sz, int len)
{
    int i = 0;
    
    if ( strlen(sz) <= len ) return sz;
    
    for (i=0; i<len; i++)
    {
        if ( IsHangle(sz[i]) )    
        {
            if ( len-1 < i+1 ) break;
            else  i++;
        }
    }
    
    sz[i] = 0;
    
    return sz;
};

int main()
{
    char *szOrg="11가핳a안녕하세요abcdef우하하1";
    char buf[256]="";
    int i;

    printf("Org: %s(%d)\n", szOrg, strlen(szOrg) );
    for(i=0; i<=strlen(szOrg); i++)
    {
        strcpy( buf, szOrg );
        printf( "%02d, %s\n", i, left(buf, i) );
    }
    return 0;
}

DisplayHex


패킷 또는 버퍼를 파라미터로 받아 16진수 형태로 출력을 예쁘게 해준다.

* 사용예
DisplayHex( "Send Packet", szBuf, sizeof(szBuf) );

* 결과
[ Send Packet ] =========
-----------------------------------------------------------------------
ADDR  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF
-----------------------------------------------------------------------
0000  54 65 73 74 20 50 61 63 6B 65 74 20 53 61 6D 70  Test Packet Samp 
0010  6C 65 20 44 61 74 61 2E 2E 2E 2E 2E 20 5E 5E 00  le Data..... ^^. 
0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
0070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ 
-----------------------------------------------------------------------

* 소스
void DisplayHex( char *title, char *pPacket, int size )
{
    long    i                   = 0;
    long    j                   = 0;
    long    lTotal              = 0;
    char    szBinBuff[64]       = {0,};
    char    szASCIIBuff[18]     = {0,};
    u_char  byBuff              = 0;
    char    cpWrk[256]          = {0};
	char    szBuf[2048]			= {0,};

    printf("[ %s ] =========\n", title );
    printf("-----------------------------------------------------------------------\n");
    printf("ADDR  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF\n");
    printf("-----------------------------------------------------------------------\n");

    for (i = 0; i * 0x10 < size; i++) 
	{
        memset(szBinBuff  , 0, sizeof(szBinBuff  ));
        memset(szASCIIBuff, 0, sizeof(szASCIIBuff));
        for (j = 0; j < 0x10; j++) 
		{
            byBuff = (u_char)*(((u_char*)pPacket) + i * 0x10 + j);
            sprintf(szBinBuff   + j * 3, "%02X ", byBuff);
            sprintf(szASCIIBuff + j    , "%c"   , 
                ((byBuff >= 32) && (byBuff <= 126)) ? byBuff : '.');
            lTotal ++;
            if (lTotal >= size) 
			{
                break;
            }
        }

		sprintf( szBuf, "%04X  %-47.47s  %-17.16s\n", i * 0x10, szBinBuff, szASCIIBuff);
        printf( szBuf );
    }

    printf("-----------------------------------------------------------------------\n");
}