블로그 이미지
훅크선장

카테고리

분류 전체보기 (362)
사진이야기 (23)
펭귄컴퓨팅 (121)
컴퓨터보안 (84)
절름발이 프로그래머 (59)
C언어, C++ 과 Visual C+.. (12)
C# .net (1)
Delphi (40)
Python (5)
하드웨어개조 (23)
멀알려줄까 (35)
홈베이킹&홈쿠킹 (2)
잡다한것들 (15)
Total
Today
Yesterday

달력

« » 2024.12
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

공지사항

태그목록

최근에 올라온 글

참고한 내용은 http://www.ciuly.com/delphi/indy/  입니다.
HTTP/HTTPS 프로토콜을 델파이로 Indy 로 구현한 예제들입니다. (주로 웹 메일 로그인 부분에 집중된 내용)

가장 중요한 점 HTTP/HTTPS의 Get 요청에 대한 응답이 Indy 컴포넌트에서 대부분 exception의 ErrorMessage 로 저장된다는 점입니다.
웹 브라우저에서 보이는 것이 항상 Get 함수의 리턴 값 문자열이 아닌 예외 상황으로 많이 처리되는 것 때문에 상당히 어려움을 겪었습니다.

위 구문을 추가하시고, 다음과 같이 코드를 작성하면 됩니다.

아래 코드에서 memo 컴포넌트에 html, response_cdoe, err_code, err_msg 를 출력해보면, 감이 오시리라 생각합니다.
html만 중요한 게 아니라, err_ 쪽도 상당히 중요합니다. 처리가 항상 필요합니다.

unit Unit1;

interface

uses
  Classes, SysUtils,
  IdHTTP, IdSSL, IdSSLOpenSSL;

implementation
procedure btn1click(IP:string);
var
  idhttps: TIdHTTP;
  sslIOHandler : TIdSSLIOHandlerSocketOpenSSL;
  URL, html, response_code, err_code, err_msg : string;
  MemoryStream: TMemoryStream;
  rbstr: RawByteString;
begin
  try
    idhttps := TIdHTTP.Create();
    sslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    try
      sslIOHandler.SSLOptions.Method := sslvSSLv23;
      sslIOHandler.SSLOptions.Mode := sslmClient;

      idhttps.IOHandler := sslIOHandler;
      idhttps.HandleRedirects:=False;
      idhttps.ConnectTimeout := 5000;
      idhttps.ReadTimeout := 5000;

      URL := 'https://' + IP + '/index.html';

      try
        MemoryStream := TMemoryStream.Create;
        try
          try
            // HTTP Get Method
            idhttps.Get(url, MemoryStream);

            rbstr := PAnsiChar(MemoryStream.Memory);
            if (Pos('utf-8', idhttps.Response.ContentType)=0) and (AnsiPos('charset=utf-8', string(rbstr))=0) then
              SetCodePage(rbstr, 949, false)
            else
              SetCodePage(rbstr, 65001, false);
            response_code := IntToStr(idhttps.Response.ResponseCode); // HTTP 응답 코드
            html := string(rbstr); // 정상적인 페이지 응답은 여기에 저장

          except on e: EIdHTTPProtocolException do
            begin
              err_code := IntToStr(e.ErrorCode); // 예외 상황의 에러 코드 (idhttps.Response.ResponseCode 와 거의 동일)
              err_msg := e.ErrorMessage;  // 예외 상황의 메시지, 웹 페이지 상에 보여주는 HTML 페이지 내용
            end;
          end;
      finally
        MemoryStream.Free;
      end;
      except on E:Exception do

      end;

    finally
      idhttps.Disconnect;

      sslIOHandler.Free;
      idhttps.Free;
    end;

  except on E:Exception do

  end;

end;

end.
Posted by 훅크선장
, |
특정 일련 숫자들로부터 임의 순열(즉, 무작위로 뒤섞인 일련의 숫자들)을 만드는 방법입니다.

다음의 예제는 1부터 100까지의 숫자가 일련되어 저장된 배열에서, 순서가 무작위로 뒤바뀐 배열을 만들어냅니다.
SizeofArr 을 바꿔서, 다양한 일련의 숫자를 뒤섞을 수 있습니다.

procedure TForm1.btn2Click(Sender: TObject);
var
  RandomPermutationArray : array of Integer;
  I, J, SizeofArr, RandomPosition, Temp : Integer;
  PrintStr : String;
begin
  SizeofArr := 100;
  SetLength(RandomPermutationArray, SizeofArr + 1); // We need one more space, because number starts 1.

  // array init
  for I := 1 to SizeofArr do
    RandomPermutationArray[I] := I;

  // randoomize Permutation
  for J := 1 to  SizeofArr - 1 do
  begin
    RandomPosition := J + Random(SizeofArr - J) + 1;
    Temp := RandomPermutationArray[J];
    RandomPermutationArray[J] := RandomPermutationArray[RandomPosition];
    RandomPermutationArray[RandomPosition] := Temp
  end;

  PrintStr := '';
  for I := 1 to SizeofArr do
  begin
    PrintStr := PrintStr + ' ' + IntToStr(RandomPermutationArray[I]);
  end;
  edt3.Text := PrintStr;
end;

Posted by 훅크선장
, |
MaxMind 에서 http://www.maxmind.com/app/geolitecountry 통하여 제공하고 있는

IP를 소재 국가 및 도시로 바꾸어주는 Open Source API가 있습니다.

해당 API에 파스칼(Pascal) 이 지원되는데, 이 파일에 오래전에 만들어진 것이라서,

현재 RAD Studio 계열에서는 컴파일 에러가 발생합니다.

데이터베이스 파일은 그대로 사용합니다만,
제공되는 GeoIP.pas 소스 파일은 일부 수정이 필요합니다.

수정이 필요한 부분은 다음과 같이 두 부분입니다.
http://forum.maxmind.com/viewtopic.php?t=1204 에 있는 내용을 참고하였습니다.

1번 :

function TGeoIP._GetCity(IPNum: Cardinal; var GeoIPCity: TGeoIPCity): TGeoIPResult;
var
  ...
// p: PChar;
   p: PAnsiChar;
  ...


2번 :

function TGeoIP.AddrToNum(const IPAddr: string): Cardinal;
var
//netlong: LongInt;             // signed! - wrong type for comparison with INADDR_NONE
  netlong: Cardinal;            // unsigned   (or Longword)
begin
//netlong := inet_addr(PChar(IPAddr));
  netlong := inet_addr(PAnsiChar(AnsiString(IPAddr)));
  if netlong <> INADDR_NONE then



Posted by 훅크선장
, |
운영 체제에서는 time_t를 32 비트나 64 비트 부호있는 정수형으로 정의하며, UTC 1970-1-1 0:00:00 이후 경과한 를 담고 있다. (이것이 바로 Unix Time 이라고 불린다.)

이것을 작성하게 된 계기는 PE 파일 헤러의 TimeDateStamp 값을 현재 시간으로 변환하는 과정에서 알게된 사실을 기억해두기 위해서이다. - 처음에는 변환 함수가 없는 것으로 생각했다가, 나중에 알고보니 UnixToDateTime() 함수가 있었다. 문제는 이 날짜,시간 관련 함수들은 DateUtils 유닛을 사용해야 한다.

수동으로 구현한 UnixToDateTime() 함수는 다음과 같다.

var
  dt: TDateTime;
  tzinfo: TTimeZoneInformation; // UTC 시간을 GMT 시간으로 변환하기 위해서,
  GMT_Plus_Offset: Double;

begin
  TSEdit.Text:=IntToHex(ImageNTHeaders.FileHeader.TimeDateStamp,8); // PE 헤더에 있는 TimeDateStamp 값을 가져온다.
  tFlag:=ImageNTHeaders.FileHeader.TimeDateStamp;

  case GetTimeZoneInformation(tzinfo) of // 현재 컴퓨터에서 타임존 정보를 읽어온다.
    TIME_ZONE_ID_STANDARD: GMT_Plus_Offset := -(tzinfo.StandardBias + tzinfo.Bias) / (24*60); // 표준 타임존 일 경우,
    TIME_ZONE_ID_DAYLIGHT: GMT_Plus_Offset := -(tzinfo.DaylightBias + tzinfo.Bias) / (24*60); // 일광절약시간을 적용하는 경우,
  else // 매우 일반적인 경우,
    GMT_Plus_Offset := -(tzinfo.Bias) / (24*60);  // 한국의 경우에는 Bias 값이 -540 으로 9시간 * 60 분으로 지정되어 있다. 이것을 TTimeDate 객체로 변환하려면, 날짜 수로 변환해야 되서, 60분과 24시간으로 나누어준다.
  end;

  dt := EncodeDate(1970, 1, 1) + (tFlag / SecsPerDay) + GMT_Plus_Offset; // 변환 완료

  edtTimeDateStampStr.Text := DateTimeToStr(dt); // GMT+9 시간을 출력한다.

end;

위에서 tzinfo를 얻는 과정을 제외하면, TimeDateStmap 값을 UTC 시간으로 변환할 수 있다. (너무 쉽다.)

Posted by 훅크선장
, |
array of char의 경우는 바로 AnsiString이나 String으로 변환되지만,
array of BYTE의 경우는 char 과는 틀리게 변환하는 방법이 따로 있었습니다.

상당히 정보 검색에 어려움이 있었지만, 테스트한 결과 다음과 같이 합니다.
const
 Name_Length = 16;
var
 Name : array[0..15] of BYTE;
 strAnsiName : AnsiString;
 strName : String;
begin
  SetString(strAnsiName, PAnsiChar(@Name[0]), Name_Length);
  strName := string(strAnsiName);
end;


Posted by 훅크선장
, |
이전에 Delphi에서 inline assembly를 사용하는 방법에 대해서 글을 하나 썼는데,
C++ Builder에서도 거의 비슷하게 사용할 수 있다는 점을 알아서 그 내용을 정리했다.
Delphi 와는 다른 점이 꽤 있다.

1. 함수 인자값들은 반드시 레지스터에 저장해서 연산하여야 한다. 함수 인자는 바로 연산문에 사용할 수 없다. (지역변수는 변수명으로 바로 사용할 수 있다.)
* 참고: 함수 인자들은 EBP를 기준으로도 접근할 수 있습니다. 함수인자는 앞에서부터 차례로 DWORD PTR [EBP+0Ch], DWORD PTR [EBP+10h], DWORD PTR [EBP+14h] 로 접근 가능합니다.
2. 함수 인자로 주어지는 포인터 변수는 반드시 로컬변수로 재지정해서만 접근하여야 합니다.
3. 함수가 리턴값을 가져야 된다면, 별도로 리턴값을 위한 변수를 지정해서 사용하여야 합니다.
4. 올리디버거에서 추출된 어셈블리 코드안의 숫자값은 모두 16진수 이므로, 인라인 어셈블리 코드로 사용하려면, 모든 숫자값에는 16진수 임을 알리는 “h” 문자를 추가하여야 한다. (MOV EAX, 20 이렇게 되어 있다면, 반드시 MOV EAX, 20h 로 바꾸어야 한다.)
5. 문자열은 주소값으로 연산되므로, 포인터 연산에 주의를 해야한다.(너무나도 당연한 말이다.)

unsigned int TMainForm::keygen1(AnsiString aStr, int aStr_len, int *ret)
{
  int ret2; 
  int *ptr_ret = ret;
  asm
  { 
XOR EDI, EDI // init value := 0
XOR ESI, ESI
XOR EDX, EDX
XOR ECX, ECX  // ecx := 0, init value
XOR EBX, EBX  // ebx := 0, init value
XOR EAX, EAX
MOV EDX, aStr_len // copy string length
MOV ESI, [aStr] // load string address 
MOV EDI, ptr_ret // load integer value address

  @@LOOP: // Label
MOVSX EAX, BYTE PTR [ESI+ECX]
INC ECX
XOR EAX, ECX
ADD EBX, EAX
CMP ECX, EDX
JNZ @@LOOP  // conditional jump to Label
IMUL EAX, EAX, 6h
MOV DWORD PTR [EDI], EBX // pointer variable value copy
SHL EBX, 7h
MOV ret2, EBX  // local variable value copy
ADD EAX, EBX

MOV ret2, EAX // return value
  }

  return ret2;
}
Posted by 훅크선장
, |
얼마전 받은 교육이 “악성코드 분석” 이었는데, 교육중에 주로 악성코드보다는 초급자를 위한 Crackme 나 KeygenMe 프로그램을 주로 분석하있다.
간단하게 Keygen을 만들려고 하니, 일일히 어셈블리 코드를 분석해서, C 언어나 Python 으로 변환하기가 너무 귀찮은 경우가 생겨서, 델파이에 인라인 어셈블리를 적용하는 방법을 찾아내었다.
알고보니 너무 간단하고, 참 쉽다.
asm
...
end;
지시어 안에, 어셈코드를 넣기만 하면 된다.

그러나, 올리디버거(OllyDbg v1.10)에서 가져온 어셈블리 코드를 델파이에 적용하는 경우에 반드시 주의하여야 하는 사항은 다음과 같다.
1. 함수 인자값들은 반드시 레지스터에 저장해서 연산하여야 한다. 함수 인자는 바로 연산문에 사용할 수 없다. (지역변수는 변수명으로 바로 사용할 수 있다.)
* 참고: var 지정(내부에서 변하는 변수)이 아닌 일반 함수 인자들은 EBP를 기준으로 접근할 수 있습니다. 인자값이 변하지 않는 상수라면, 레지스터에 복사하지 않고 바로 사용할 수 있습니다. 문자열을 인자로 주었다면, 꼭 레지스터에 복사해야 합니다.
함수인자는 뒤에서 부터 차례로 DWORD PTR [EBP-04h], DWORD PTR [EBP-08h, DWORD PTR [EBP-0Ch] 로 접근 가능합니다.
2. 어셈블리 코드안에서 사용하는 모든 레지스터는 반드시 사용전에 PUSH 하고, 사용후에 POP 해야한다. (레지스터를 초기화하는 것도 오류를 없애기 위해서는 당연히 사용해야 한다.)
3. 올리디버거에서 추출된 어셈블리 코드안의 숫자값은 모두 16진수 이므로, 인라인 어셈블리 코드로 사용하려면, 모든 숫자값에는 16진수 임을 알리는 “h” 문자를 추가하여야 한다. (MOV EAX, 20 이렇게 되어 있다면, 반드시 MOV EAX, 20h 로 바꾸어야 한다.)
4. 문자열은 주소값으로 연산되므로, 포인터 연산에 주의를 해야한다.(너무나도 당연한 말이다.)


예제 코드를 보여본다. (함수내에서 인자값을 바꾸는, 포인터 연산도 포함되어 있다.)
-------------------------------------------------------------------------
function TMainForm.keygen1(aStr: AnsiString; aStr_len: Integer; var ret:Integer):Cardinal;
var
  ret2 : Integer;
begin
  asm
    PUSH EDX // Safe Store before use
    PUSH ECX
    PUSH EBX
    PUSH EAX
    PUSH ESI
    PUSH EDI

    XOR EDI, EDI // init value := 0
    XOR ESI, ESI
    XOR EDX, EDX
    XOR ECX, ECX  // ecx := 0, init value
    XOR EBX, EBX  // ebx := 0, init value
    XOR EAX, EAX
    MOV EDX, aStr_len // copy string length
    MOV ESI, [aStr] // load string address
    MOV EDI, [ret] // load integer value address

  @@LOOP: // Label
    MOV AL, BYTE PTR [ESI+ECX]
    INC ECX
    XOR EAX, ECX
    ADD EBX, EAX
    CMP ECX, EDX
    JNZ @@LOOP  // conditional jump to Label
    IMUL EAX, EAX, 6h
    MOV DWORD PTR [EDI], EBX // pointer variable value copy
    SHL EBX, 7h
    MOV ret2, EBX  // local variable value copy
    ADD EAX, EBX

    MOV @Result, EAX // return value : Result

    POP EDI // restore value
    POP ESI
    POP EAX
    POP EBX
    POP ECX
    POP EDX
  end;
end;
Posted by 훅크선장
, |
http://www.delphipraxis.net/147190-how-work-wic.html 를 참고하였습니다.

WIC 라는 개념이 Delphi 2010부터 지원됩니다. 여러 종류의 그래픽 파일들의 자유로운 읽기, 쓰기, 변환이 지원된다고 합니다. RAW 파일까지도 자유롭게 다룰 수 있다고 합니다.

아래와 같은 소스를 만들어서 테스트해보시면 알 수 있습니다. 중요한 점은 Wincodec 을 uses 구문에 추가하는 것입니다.
-------------------------------------------------------------------------------------------------------------
unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Wincodec;

type
  TForm1 = class(TForm)
    OpenPictureDialog: TOpenDialog;
    btnOpen: TButton;
    edtIamgeFilePath: TEdit;
    Image: TImage;
    procedure btnOpenClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// Function() to test if this engine working correctly
function WICImageFormatDescription(const WIC: TWICImage): string;
begin
  Result := '';

  case WIC.ImageFormat of
    wifBmp: Result := 'Bitmap';
    wifPng: Result := 'PNG';
    wifJpeg: Result := 'JPEG';
    wifGif: Result := 'GIF';
    wifTiff: Result := 'TIFF';
    wifWMPhoto: Result := 'JPEG XR';
    wifOther:
    begin
      if GUIDToString(WIC.EncoderContainerFormat) = GUIDToString(GUID_ContainerFormatIco) then
        Result := 'Icon'
      else
        Result := 'other'
      ;
    end;
  end;
end;


procedure TForm1.btnOpenClick(Sender: TObject);
var
  WIC: TWICImage;
begin
  WIC := TWICImage.Create;
  try
    if OpenPictureDialog.Execute then
    begin
      edtIamgeFilePath.Text := OpenPictureDialog.FileName;
      WIC.LoadFromFile(OpenPictureDialog.FileName);
      Image.Picture.Assign(WIC);
      ShowMessage(WICImageFormatDescription(WIC));
      WIC.ImageFormat := wifWMPhoto;
      WIC.SaveToFile('output.wdp');
    end;
  finally
    WIC.Free;
  end;
end;

end.
Posted by 훅크선장
, |
델파이에서도 GZip 압축을 풀 수 있습니다.
GZip 압축은 .gz 확장자를 가지는 파일로 볼 수 있습니다.

공개된 zlib를 사용하는 것이 가장 좋습니다.
다만, zlib는 원래 GZip 용이 아니고, .Z 확장자를 가지는 압축파일을 다루는 라이브러리입니다.
델파이용 zlib는 다음 링크에서 받을 수 있습니다.

zlib 사용법에 대해서는 다른 분께서 이미 설명서를 잘 쓰셨습니다.

다만, zlib를 이용하여, GZip 압축을 푸는 방법이 잘 안 알려져 있을 뿐입니다.

다음과 같이 하면, 스트림을 이용하여, GZip 파일 압축을 풀 수 있습니다.
-- 에러 처리를 추가하였습니다. GZ 함수 계열이 모두 procedure 이다보니, 에러처리가 별도로 필요합니다.  --

use ZlibEx, ZlibExGZ;
...
...
procedure TForm1.btn3Click(Sender: TObject);
var
  InputFileName, OutputFilename : string;
  InputStream : TFileStream;
  OutputStream : TMemoryStream;
  OutputSize : Int64;
begin
  InputFileName := edt1.Text;
  OutputFilename := edt2.Text;
  InputStream := TFileStream.Create(InputFileName, fmOpenRead);
  try
    OutputStream := TMemoryStream.Create;
    try
try
      GZDecompressStream(InputStream, OutputStream);
      OutputSize := OutputStream.Size;
except
  on E : Exception do
  begin
mmo1.Lines.Add(E.ClassName+' error raised, with message : '+E.Message);
OutPutSize := 0;
  end;
end;

      mmo1.Lines.Add(IntToStr(OutputSize));

      OutputStream.SaveToFile(OutputFileName);
    finally
      OutputStream.Free;
    end;
  finally
    InputStream.Free;
  end;
end;

굳이 스트림을 사용하는 이유는 파일내용을 단순히 파일로 저장하기 보다는, 스트림으로 다른 곳에서 사용하는 경우가 더 많기 때문입니다.
그래서 일부러 위 예제에서는 메모리 스트림을 사용했습니다. 파일 스트림을 사용해도 결과는 동일합니다만, 스트림 생성때 인자가 틀립니다.

출력파일을 위한 파일스트림은 이렇게 생성합니다.
OutputStream := TFileStream.Create(OutputFileName, fmCreate);

단순히 파일로만 압축을 풀어서 저장하고 싶다면, 간단하게 함수 한번 호출로 끝납니다.

GZDecompressFile(InputFileName, OutputPath);

스트림과는 다르게, 파일에서 파일로 압축해제는 입력값이 압축된 파일과 압축파일이 풀려질 디렉토리  즉 경로만 필요합니다.


Posted by 훅크선장
, |
http://www.torry.net/quicksearchd.php?String=faststrings&Title=Yes
에 있는 FastStrings 모듈을 이용하면, Split() 함수가 있지만,

델파이 2009 이상에서는, 유니코드 지원때문에 위 모듈을 사용할 수 없다.

나중에 알게된 사실이지만, 위의 Split 함수는 TStrings를 사용하므로, 폼과 관련된 객체를 사용하지 않는다면 쓸 수 없다고 합니다. 완벽하게 확인되지는 않았지만, 문제가 발생한 경우를 당해봤습니다.

그래서, 함수를 만들어서 사용할 수 있다.

참고한 곳은

// 문자열을 구분자(문자)를 기준으로, 분해하는 함수
// 사용전에, 반드시 TStringList를 생성하고, 사용후 free 해야됨.
procedure Split(Input: string; const Delimiter: Char; var Strings: TStringList) ;
begin
   Assert(Assigned(Strings)) ;
   Strings.Clear;
   ExtractStrings([Delimiter], [' '], PChar(Input), Strings);
end;

여기까지가 아주 안전한 방법입니다.



-------------------------------------------------------------------------
이것은 이전에 작성한 내용으로 참고할 만하지만, 그렇게 쓸모 있지는 않습니다.

참고한 곳은


procedure Split(const Input: string; const Delimiter: Char; const Strings: TStrings);
begin
  Assert(Assigned(Strings));
  Strings.Clear;
  Strings.Delimiter := Delimiter;
  Strings.DelimitedText := Input;
end;

아예,
http://delphi.about.com/cs/adptips2002/a/bltip0902_2.htm
이렇게 직접 토큰화 함수를 만들어서 할 수도 있군요.


Posted by 훅크선장
, |