2009-12-22

Web services API design suggestion ???

Trước khi chuẩn bị design một web services API langxang đã đọc và tham khảo rất nhiều resources trên net. Mục đích muốn focus vào RESTful hơn là SOAP nhưng vấn đề nan giải nhất là security. Mới thử một suggestion nhưng vẫn còn nhiều chuyện phải nghĩ ... nói chung chẳng có gì hoàn hảo cả, chắc phải nghĩ vậy thôi.

Mình đã từng đọc đâu đó trên InfoQ câu này
REST is simple. Applying Enterprise-class Security is not.

1. Giới thiệu

Web services và format support

Tạo một bộ RESTful web services hỗ trợ XML và JSON qua HTTP.

Cơ chế authenticate và authorization

Các yếu tố cần suy nghĩ đến là user experience UX[1], OpenID support (federate-authentication) cũng như resources trong thời điểm hiện tại.
Yêu cầu đặt ra là một cơ chế flexible, lightweight. Tất nhiên không thể tốt hoàn toàn nhưng cần đạt mức chấp nhận được (về security, thời gian thực hiện ...).
Suggestion là một mô hình dạng token signing lấy ý tưởng từ Flickr API (thay vì dùng OAuth). Trong thời gian tới sẽ cố gắng hoàn thiện hệ thống API, có thể thực hiện support OAuth trong thời gian tới.

Các thành phần cần thực hiện

  1. Thiết kế và định nghĩa các APIs.
  2. Server viết bằng PHP thực hiện các chức năng: đăng ký API key, xử lý authenticate, xử lý authorization và RESTful request.
  3. Library C# SDK wrap các API
  4. Library PHP SDK wrap các API (optional)
2. Cơ chế authenticate và authorization (tham khảo Flickr Authentication API[3])

Authentication prerequisites

Tất cả các ứng dụng muốn sự dụng Authenticate API phải đăng ký một API key (đáp ứng yêu cầu có thể monitor ứng dụng) và phải thiết lập các thông số sau để có thể có API key
  1. Application title
  2. Application description
  3. Application logo graphic (optional)
  4. Application ’about’ URL (for example application introduction and download, optional)

Authentication methods

Sau khi đăng ký API key, ứng dụng sẽ được cung cấp secret key dùng trong quá trình signing (signing request process). Ứng dụng sẽ chọn 1 trong 2 methods chứng thực – web based hoặc non-web based.
Hiện tại sẽ support dạng non-web based authentication. Tương lai sẽ mở rộng hỗ trợ 3 loại như sau: web based authentication, non-web base authentication và mobile authentication.

3. Authentication dành cho non-web based application

Hiện tại Flickr sử dụng message digest algorithms MD5[7] theo đó desktop và non-web based application sẽ thực hiện giao tiếp với server để nhận được token. Tuy nhiên với những hạn chế của message digest algorithms MD5 có thể phải thay đổi so với cách thực hiện của Flickr[4] [5] [6]. Chi tiết xem phần signing process.
Đầu tiên desktop application thực hiện gọi API method auth.getFrob, method này yêu cầu signing (xem phần thực hiện signing). Nếu thành công method sẽ response một ‘frob’, dùng để construct một URL cho phép user thực hiện đăng nhập (launch system’s default browser) http://www.company.com/services/auth/?api_key=[api_key]&perms=[perms]&frob=[frob]&api_sig=[api_sig]
API signature là kết quả của signing process.
Permission yêu cầu user thực hiện grant, đối với trường hợp hiện tại chúng ta có thể omit. Đối với user log in vào system sẽ thực hiện cấp quyền tương ứng với user đó (yêu cầu làm rõ chức năng và cách xử lý của server).
Như vậy URL thực hiện authenticate là:
http://www.company.com/services/auth/?api_key=[api_key]&frob=[frob]&api_sig=[api_sig]
Hệ thống sẽ yêu cầu user log in với username/password hay OpenID. Tên application và mô tả của application sẽ được hiển thị (trong trường hợp quyết định permission theo role sau khi đăng nhập không cần hiển thị permissions mà ứng dụng mong muốn có). Nếu user đăng nhập thành công thay vì redirect về callback URL như web application, system sẽ hiển thị thông báo user log in thành công và hướng dẫn quay về application (và có thể close trang thông báo này).
Sau khi user thực hiện chứng thực thành công, quay lại application để báo cho application biết đã xong quá trình chứng thực. Application cần thực hiện gọi API method auth.getToken bằng cách cung cấp api_key và frob (method này yêu cầu signing, xem phần thực hiện signing).
Response sẽ bao gồm authentication token cần thực hiện các API call sau này (các thông tin khác kèm theo ví dụ summary của user profile, yêu cầu làm rõ sau).

Renewing non-web based authentication tokens

Để kiểm tra cần renewing hay có đủ permission hay không, application cần thực hiện gọi auth.checkToken. Nếu token không hợp lệ hoặc không đủ permission, application cần yêu cầu một token mới giống như các bước yêu cầu token đầu tiên.

Implementation guidelines

User profile data (bao gồm auth tokens) nên được lưu lại tại local user's 'profile', theo đó có thể cho phép switch user với cơ chế multiple profiles/users mà không thực hiện sharing auth data. Trong môi trường Windows có thể thực hiện dựa vào registry trong phần HKEY_CURRENT_USER, hay trong user's home directory đối với OSX/Unix.
Users phải được cung cấp chức năng 'logout'.

4. Authentication permissions

Application không nên yêu cầu quyền cao hơn mức cần thiết phải sử dụng. Cụ thể trong trường hợp này không quan tâm đến việc request permissions mà permissions sẽ được cung cấp tùy theo loged in user.
Cụ thể có 3 level cơ bản như sau (xem xét chỉnh sửa nếu cần thiết):
  • read - permission cho phép read private information
  • write - permission cho phép thêm, sửa và xóa metadata (bao gồm permission 'read')
  • delete - permission cho phép delete thông tin (bao gồm 'write' và 'read')

5. Authentication tokens

Mỗi authentication token được chỉ định riêng cho một user và một application tương ứng với application's api key, và chỉ được dùng tương ứng với application key này. Application có thể kiểm tra tình trạng của auth token bằng cách thực hiện API method auth.checkToken, method sẽ return tính hợp lệ của token cùng với user đã thực hiện authenticated (và permissions tương ứng nếu cần).
Authentication tokens có thể được thiết lập thời gian expire bởi user khi user thực hiện chứng thực với hệ thống. Chức năng này là chức năng nâng cao (có thể hide by default), có thể có 2 options là theo hour (1 hour) hoặc never. Chỉ duy nhất một auth token/app/user valid tại bất kỳ thời điểm nào. Application phải có trách nhiệm xử lý khi auth token expired, invalid và thực hiện renew.
Tất cả các API calls sử dụng auth token phải được signed (xem signing process).

6. Authentication frobs

Mỗi authentication frob sẽ tương ứng với một user và một application’s API key và chỉ có thể được sử dụng bởi application có API key này.
Giá trị của application frobs chỉ hợp lệ trong vòng 60 phút kể từ khi được tạo hoặc cho đến khi application thực hiện gọi auth.getToken.
Chỉ một application frob tương ứng với một application và một user được coi là hợp lệ tại bất kỳ thời điểm nào. Application phải có trách nhiệm xử lý khi auth token expired, invalid và thực hiện renew.

7. Signing

Tất cả các API call sử dụng auth token phải được ký tên (signed). Ngoài ra khi thực hiện các method auth.* methods và khi thực hiện chuyển hướng (redirections) tới trang chứng thực của hệ thống (system’s auth page) các yêu cầu này cũng phải thực hiện signing.
Như đã đề cập về hạn chế của message digest algorithms đặc biệt là MD5 hash, quá trình signing sẽ thực hiện khác với Flickr.
Các hướng có thể thực hiện là keep auth token secret bằng SSL, dùng auth token để signing hay coi auth token như là password bằng cách sử dụng HTTP digest authentication (RFC2617).
Một giải pháp khả thi là sử dụng một secure MAC implementations như HMAC-SHA1[8] (được hỗ trợ bởi hầu hết các programming language kể cả client và server side). Key của HMAC-SHA1 có thể sử dụng là shared secret.
Quá trình signing thực hiện theo các bước như sau:
  1. Thực hiện tạo danh sách các đối số thực (argument list) và sắp xếp theo alphabetical order dựa theo tên của tham số parameter name của từng argument. Có thể cần sẽ thực hiện normalize parameter (sẽ hướng dẫn cụ thể tham khảo Amazon Web Service, OAuth)
Ví dụ: foo = 1, bar = 2, baz = 3 sẽ được sắp xếp thành bar = 2, baz = 3, foo = 1
  1. Concatenate shared secret với các cặp argument dạng name/value (argument name/value pairs). Ví dụ: shared secret là BANANA concatenated argument list string là bar=2&baz=3&foo=1. Dùng shared secret BANANA làm key của HMAC. Kết quả có thể có format dạng base64 hoặc hexa string. Ví dụ: kết quả signature với giả sử format là base64 như sau Dcpc3Uf85y1VvZFs276KvGtum5Q=
  2. Append kết quả này vào argument list với parameter name là api_sig
Ví dụ: api_sig= Dcpc3Uf85y1VvZFs276KvGtum5Q=

8. Example cho non-web based application

  1. User nhấn login button trên application
  2. Application gọi background call tới server để nhận frob, call auth.getFrob ví dụ như sau http://www.company.com/services/rest/?method=auth.getFrob&api_key=987654321&api_sig=Dcpc3Uf85y1VvZFs276KvGtum5Q=
  3. System return frob ‘1a2b3c4d5e’
  4. Application launch system’s default browser với URL tạo được từ frob như sau http://www.company.com/services/auth/?api_key=987654321 &frob=1a2b3c4d5e&api_sig= Cdpc3Uf85y1VvZFs276KvGtum5Q=
  5. Nếu user chưa loged in vào web site, user sẽ được hỏi có thực hiện log in hay không.
· User có thể đăng nhập bằng log in form(username/password)
· User có thể chọn đăng nhập bằng OpenID,khi đó sẽ thực hiện theo flow authentication bằng OpenID
  1. Sau khi đăng nhập thành công user sẽ được hỏi có cho phép ứng dụng sử dụng các thông tin private hay không (trong trường hợp ứng dụng cụ thể thì private data là gì cần định nghĩa rõ)
  2. User chọn ‘Yes’ web site sẽ update auth database.
  3. User được hướng dẫn quay lại application và có thể đóng web page.
  4. User quay lại application và nhấn button ‘Continue’ (nút log in button trong bước log in có thể chuyển thành continue button)
  5. Application thực hiện background call tới auth.getToken, ví dụ http://www.company.com/services/rest/?method=auth.getToken&api_key=987654321&frob=1a2b3c4d5e&api_sig= Depc3Uf85y1VvZFs276KvGtum5Q=
  6. System trả về auth token ‘9765984’
  7. Application có thể thực hiện các background API call với auth token (signing với key là shared secret) ví dụ http://www.company.com/services/rest/?method=user.getProfile&api_token= 9765984&api_sig= Efpc3Uf85y1VvZFs276KvGtum5Q=

9. References









2009-11-10

Just fight for your life ...

People say "Programming is for fun, not for money" and many people get in programming with this idea. Sometimes I have the same thought but is it really true? Programming is neither inherently fun or not fun. Career path, the lack of a programming-centric. I can't work for a little of money or work without any of the potential benefits.

Faster, better, cheaper but how... Almost programs you developed are incomplete softwares, just-runable things. Even you don't know what they are. So you develop/create what or you're just forced to write something you don't like. Really fun?

Many companies think the good way to encourage smart developers is moving them elsewhere, 'moving into management'. New opportunities, new challenges? Maybe. But I don't think experienced developers really want to move into management. Developers do that because they feel a lot of pressure while companies often don't know what to do with their developers. And the most stupid thing about this is a really good developer is worth far more than any low or middle level manager.

All fool things, I won't care. Now I'll looking for really exciting programs and enjoy lots of new thing. Try to get enough money with bored job. Professional programming is a profession and has its good points and bad points. So fight for your fun...

2009-09-19

Sử dụng Detours để tìm lua_State cho AutoPlay

Bài viết mô tả việc dùng Detours
Software package for re-routing Win32 APIs underneath applications. Under commercial release for over 10 years, Detours is licensed by over 100 ISVs and used within nearly every product team at Microsoft.
để thực hiện API hooking, cụ thể là ReAlloc nhằm lấy được base address của lua_State dùng cho AutoPlay. Lý do để thực hiện việc này là không thể dò lua_State bằng cách thông thường dùng CheatEngine (xem lại bài Sử dụng Cheat Engine để search base address dùng cho auto play).

Bài viết này không đi sâu chi tiết vào Detours, chỉ mô tả cách sử dụng Detours trong trường hợp ko dùng được CheatEngine. Cách thực hiện này có thể áp dụng trong các trường hợp có logic tương tự.

Do mấy bữa nay bận nên không thể ngồi dò base address của lua_State nên không sửa hoàn chỉnh cái auto Tru Tiên. Hôm nay cuối tuần mình sẽ ngồi viết tutorial cho phần này. Sau này nếu có ai muốn tìm giá trị của lua_State hay những địa chỉ trong trường hợp tương tự thì vẫn biết đường mà mò.

Build Lua library

Đầu tiên mình check phiên bản Lua mà game đang sử dụng (là 5.1.1), sau đó phải kiếm 1 bản lua về tại http://www.lua.org/download.html. Bản mới nhất hiện tại là 5.1.4, có thể dùng bản này hoặc 5.1.1. Sau khi download về và extract trong 1 folder Lua-5.1.1. Sau đó tạo một project static library Lua-5.1.1.vcproj, add các files trong thư mục src (nhớ bỏ 2 file lua.c và luac.c là 2 files build lua command line) vào project.

Mục đích là build static library Lua. Có thể tạo solution riêng để build lua library 1 lần thôi.















Sau khi build xong có lua-5.1.1d.lib và lua-5.1.1.lib, có thể copy lên thư mục Lib để dễ reference.

Cách thức cấp phát lua_State

Tạo một project đơn giản dạng Win32 application (dùng cái này khỏi dính dáng đến MFC) để biết lua_State được cấp phát như thế nào. Tạo Win32 application viết dạng Dialog

// Test.cpp : Defines the entry point for the application.

#include "stdafx.h"

// Header for Lua library
extern "C" {
#include "../Lib/Lua-5.1.1/src/lua.h"
#include "../Lib/Lua-5.1.1/src/lauxlib.h"
#include "../Lib/Lua-5.1.1/src/lualib.h"
};

// 21/Nov/2009 fixed link to Lua library
#if defined(_DEBUG)
// Debug libraries
#pragma comment(lib, "../Lib/lua-5.1.1d.lib")
#else
// Release libraries
#pragma comment(lib, "../Lib/lua-5.1.1.lib")
#endif

HINSTANCE g_hInstance = NULL;
BOOL CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPSTR lpCmdLine, int nCmdShow)
{
  // TODO: Place code here.
    g_hInstance = hInstance;
 
    HWND hDialog = 0; 
    hDialog = ::CreateDialog(hInstance, MAKEINTRESOURCE(IDD_TEST_DLG), 0, DialogProc); 
    if (!hDialog)
    {
  LPVOID lpBuffer;
  ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
   NULL, GetLastError(), 0, (LPTSTR)&lpBuffer, 0, NULL);

        ::MessageBox(NULL, (LPTSTR)lpBuffer, _T("CreateDialog"), MB_ICONEXCLAMATION | MB_OK);
  ::LocalFree(lpBuffer);

        return 1;
    } else {
  ::ShowWindow(hDialog, nCmdShow);
 }
 
    MSG  msg;
    int status;
    while ((status = ::GetMessage(&msg, 0, 0, 0)) != 0) {
        if (status == -1) {
            return -1;
  }

        if (!::IsDialogMessage(hDialog, & msg)) {
            ::TranslateMessage( & msg );
            ::DispatchMessage( & msg );
        }
    }
 
    return msg.wParam;
}

BOOL CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 WORD wId, wEvent;
 HICON hIcon = NULL;
 lua_State* state = NULL;
 TCHAR buffer[1024] = { '\0' };

    switch (message)
    {
    case WM_INITDIALOG:   
  hIcon = ::LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ICON));
  if(hIcon) {
   ::SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
  }
        return TRUE;

    case WM_COMMAND:
        wId    = LOWORD(wParam); 
  wEvent = HIWORD(wParam); 

  // Parse the menu selections:
  switch (wId)
  {
   case IDC_BUTTON_TEST:
    state = luaL_newstate();
    wsprintf(buffer, _T("[Process %p] "), ::GetCurrentProcessId(), state);
    ::MessageBox(NULL, buffer, _T("Test"), MB_OK);

    if (state != NULL) {
     lua_close(state);
    }
    
    break;

   case IDOK:
    ::DestroyWindow(hWnd);
    break;

   case IDCANCEL:
    ::DestroyWindow(hWnd);
    break;

   default:
    return ::DefWindowProc(hWnd, message, wParam, lParam);
  }
        return TRUE;

    case WM_DESTROY:
        ::PostQuitMessage(0);
        return TRUE;

    case WM_CLOSE:
        ::DestroyWindow(hWnd);
        return TRUE;
    }
    return FALSE;
}

Test project đơn giản chỉ là một dialog có một nút test.
// Header for Lua library
extern "C" {
#include "../Lib/Lua-5.1.1/src/lua.h"
#include "../Lib/Lua-5.1.1/src/lauxlib.h"
#include "../Lib/Lua-5.1.1/src/lualib.h"
};

//  21/Nov/2009 fixed link to Lua library
#if defined(_DEBUG)
// Debug libraries
#pragma comment(lib, "../Lib/lua-5.1.1d.lib")
#else
// Release libraries
#pragma comment(lib, "../Lib/lua-5.1.1.lib")
#endif
File lua-5.1.1d.lib là Lua đã build trong bước trước.



Khi thực hiện nhấn button Test sẽ tạo một lua_State, do đó để break point ngay đây xem có được thông tin gì không. Mình biết lua_State được tạo bởi luaL_newstate.

// Parse the menu selections:
switch (wId)
{
 case IDC_BUTTON_TEST:
  state = luaL_newstate();

Thực hiện F11 step into sẽ vào lua_newstate xem tiếp

LUALIB_API lua_State *luaL_newstate (void) {
  lua_State *L = lua_newstate(l_alloc, NULL);
  if (L) lua_atpanic(L, &panic);
  return L;
}

Tiếp tục step into lua_newstate với l_alloc
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
  int i;
  lua_State *L;
  global_State *g;
  void *l = (*f)(ud, NULL, 0, state_size(LG));
  if (l == NULL) return NULL;
Mình để ý tiếp lua_Alloc và step into lua_Alloc f (line 5), step into lua_Alloc tại bước này
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
  (void)ud;
  (void)osize;
  if (nsize == 0) {
    free(ptr);
    return NULL;
  }
  else
    return realloc(ptr, nsize);
}

Tới đây mình hầu như đã có thông tin mong muốn

  • Lua alloc sử dụng API realloc
  • Kích thước cấp phát là nzine

Có nghĩa là lua_State là giá trị trả về của realloc với size là 376. Con số này rất quan trọng mình sẽ ghi nhớ.

Hình: Kích thước cấp phát của lua_State

Tìm giá trị lua_State của game thông qua API hooking

Vấn đề tiếp theo là làm sao lấy được giá trị trả về của realloc trong game. Khi game bắt đầu khởi động thì lua_State đã được tạo và load các script, trước cả khi ra màn hình log in.

Khoảng thời gian này rất ngắn và không thể inject dll sau đó được. Nếu game build mode debug thì mình có thể dùng chính Visual Studio để open elementclient.exe với break point là realloc with condition (đó là cách làm lúc trước). Nhân đây mới nói chẳng hiểu Tru Tiên bản cũ sao nó dám đưa build debug làm bản thương mại ???.

Cách thực hiện theo suy nghĩ là lắng nghe tất cả các cấp phát có kích thước đúng bằng kích thước của lua_State mà bước trên mình đã có 376.

Mình sẽ dùng Detours một thư viện có sẵn để thực hiện system hook và thực hiện detour realloc. Trang chính của Detours là http://research.microsoft.com/en-us/projects/detours/ với version mới nhất là 2.1. Mình không biết nó có đổi gì hay có gì mới không nên dùng 1.5 bao gồm 2 file đơn giản detour.lib và detour.h (để trong thư mục Lib).



Libraries Lua và Detours

Bước 1: Chuẩn bị Hook và Installer

Tạo solution Detours có 2 project là Hook và Installer cùng cấp với thư mục Lib.

  1. Hook project là Win32 dll
  2. Installer project giống như test project ở trên ngoại trừ có 2 nút là Install và Uninstall.

Trong Hook project sẽ cần sử dụng detours.lib và detours.h.

Bước 2: Project Hook

Trong file Hook.h thực hiện include Detours, và tạo 2 function export install và uninstall.
Mình dùng file .def để export nên tạo file Hook.def và thêm vào project (có thể dùng __declspec(dllexport) cũng được)
// -----------------------------------------------------------------------------
//  Document    :  Hook.h
//  Description :  Hook include file
//  Create date :  15/9/2009-13:40:07
//  Author      :  gponster (langxang)
// -----------------------------------------------------------------------------

#ifndef __HOOK_H_B10B0170_CAB8_4F19_8F66_830200F34112__
#define __HOOK_H_B10B0170_CAB8_4F19_8F66_830200F34112__

#include 
#include 
#include 

#if (_MSC_VER < 1310)
#else
#include 
#endif

#include "../Lib/detours.h"

#pragma warning(disable: 4099) 
#pragma comment(lib, "../Lib/detours.lib") 

#if defined(__cplusplus)
extern "C" {
#endif 
 void InstallHook();
 void UninstallHook();
#if defined(__cplusplus)
}
#endif

#endif // __HOOK_H_B10B0170_CAB8_4F19_8F66_830200F34112__

Nội dung Hook.def
; Hook.def : Declares the module parameters for the DLL.

LIBRARY "Hook"

EXPORTS
; Explicit exports can go here
InstallHook
UninstallHook

DLL này ko có gì đặc biệt, nếu bị lỗi SAFESEH thì tắt lý do là compiler mới bị lỗi khi link với lib build bằng compiler cũ (ở đây là Detours)
This happens when you link an .obj or .lib that contains code created by an earlier version of the compiler.
Tắt /SAFESEH:NO Project properties > Configuration Properties > Linker > Advanced


Mình sẽ thực hiện install và uninstall hook với ShellProc. Nếu bạn nào chưa hiểu rõ thì có thể tự tìm hiểu thêm (về API hooking và DLL Injection sẽ có thể ở một bài khác).

// -----------------------------------------------------------------------------
//  Document    :  Hook.cpp
//  Description :  Hook implementation
//  Create date :  15/9/2009-13:40:07
//  Author      :  gponster (langxang)
// -----------------------------------------------------------------------------

#include "Hook.h"

HHOOK g_hHook = NULL;
HANDLE g_hInst = NULL;

DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxA(HWND hWnd, LPCSTR lpText, 
 LPCSTR lpCaption, UINT uType), MessageBoxA);
DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxW(HWND hWnd, LPCWSTR lpText, 
 LPCWSTR lpCaption, UINT uType), MessageBoxW);

typedef void* (__cdecl *ReAllocFunc)(void* memptr, size_t newsize); 
ReAllocFunc g_lpTargetReAllocMSVCRT = NULL;   // Target realloc function pointer in MSVCRT
ReAllocFunc g_lpTrampolineReAllocMSVCRT = NULL;  // Trampoline realloc function pointer in MSVCRT

ReAllocFunc g_lpTargetReAllocMSVCR90D = NULL;  // Target realloc function pointer in MSVCR90D
ReAllocFunc g_lpTrampolineReAllocMSVCR90D = NULL; // Trampoline realloc function pointer in MSVCR90D

int WINAPI PatchedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
 CHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfA(buffer, sizeof(buffer), "%s - patched by Detours", lpText);

 return TrampolineMessageBoxA(hWnd, buffer, lpCaption, uType);
}

int WINAPI PatchedMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) {
 WCHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfW(buffer, sizeof(buffer), L"%s - patched by Detours", lpText);

 return TrampolineMessageBoxW(hWnd, buffer, lpCaption, uType);
}

#define EXPECTED_SIZE (376)
void* __cdecl PatchedReAllocMSVCRT(void* memblock, size_t newsize)
{ 
 void* result = NULL;
 result = g_lpTrampolineReAllocMSVCRT(memblock, newsize);

 if (newsize == EXPECTED_SIZE)
 {
  TCHAR buffer[1024] = { 0x00 };

  ::StringCbPrintf(buffer, sizeof(buffer), 
   _T("[Process %p] MSVCRT.realloc memptr %p size %d result %p"), 
   ::GetCurrentProcessId(), memblock, newsize, result);
  ::OutputDebugString(buffer);
 }

 return result;
}

void* __cdecl PatchedReAllocMSVCR90D(void* memblock, size_t newsize)
{ 
 void* result = NULL;
 result = g_lpTrampolineReAllocMSVCR90D(memblock, newsize);
 
 if (newsize == EXPECTED_SIZE)
 {
  TCHAR buffer[1024] = { 0x00 };

  ::StringCbPrintf(buffer, sizeof(buffer), 
   _T("[Process %p] MSVCR90D.realloc memptr %p size %d result %p"), 
   ::GetCurrentProcessId(), memblock, newsize, result);
  ::OutputDebugString(buffer);
 }

 return result;
}

void Intercept()
{
 DetourFunctionWithTrampoline((PBYTE)TrampolineMessageBoxA, (PBYTE)PatchedMessageBoxA);
 DetourFunctionWithTrampoline((PBYTE)TrampolineMessageBoxW, (PBYTE)PatchedMessageBoxW);

 g_lpTargetReAllocMSVCRT = (ReAllocFunc)DetourFindFunction("MSVCRT.DLL", "realloc");
 if(g_lpTargetReAllocMSVCRT) {
  g_lpTrampolineReAllocMSVCRT = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCRT, 
   (PBYTE)PatchedReAllocMSVCRT);
 }

 g_lpTargetReAllocMSVCR90D = (ReAllocFunc)DetourFindFunction("MSVCR90D.DLL", "realloc");
 if(g_lpTargetReAllocMSVCR90D) {
  g_lpTrampolineReAllocMSVCR90D = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCR90D, 
   (PBYTE)PatchedReAllocMSVCR90D);
 }
}

void UnIntercept()
{
 DetourRemove((PBYTE)TrampolineMessageBoxA, (PBYTE)PatchedMessageBoxA);
 DetourRemove((PBYTE)TrampolineMessageBoxW, (PBYTE)PatchedMessageBoxW);

 if(g_lpTrampolineReAllocMSVCRT) {
  DetourRemove((PBYTE)g_lpTrampolineReAllocMSVCRT, (PBYTE)PatchedReAllocMSVCRT);
 }

 if (g_lpTrampolineReAllocMSVCR90D)
 {
  DetourRemove((PBYTE)g_lpTrampolineReAllocMSVCR90D, (PBYTE)PatchedReAllocMSVCR90D);
 } 
}

BOOL APIENTRY DllMain(HANDLE hInstDLL, DWORD  dwReason, LPVOID lpReserved)
{
 g_hInst = hInstDLL;
 switch (dwReason)
 {
 case DLL_PROCESS_ATTACH:
  Intercept();
  break;

 case DLL_THREAD_ATTACH:
  break;

 case DLL_THREAD_DETACH:
  break;

 case DLL_PROCESS_DETACH:
  UnIntercept();
  break;
 }
 return TRUE;
}

LRESULT CALLBACK ShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
 return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

void InstallHook()
{
 if(g_hHook == NULL) {
  g_hHook = ::SetWindowsHookEx(WH_SHELL, ShellProc, (HINSTANCE)g_hInst, 0);
 }
}

void UninstallHook()
{
 if(::UnhookWindowsHookEx(g_hHook)) {
  g_hHook = NULL;
 }
}

Để ý hàm DllMain sẽ thực hiện Intercept khi DLL_PROCESS_ATTACH và UnIntercept khi DLL_PROCESS_DETACH.

Bước 3: làm quen với Detours

Đơn giản mình sẽ demo thử với MessageBox.
Mình patch 2 version của MessageBox là MessageBoxA và MessageBoxW.

Đầu tiên khai báo với macro của Detours DETOUR_TRAMPOLINE

DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxA(HWND hWnd, LPCSTR lpText, 
 LPCSTR lpCaption, UINT uType), MessageBoxA);
DETOUR_TRAMPOLINE(int WINAPI TrampolineMessageBoxW(HWND hWnd, LPCWSTR lpText, 
 LPCWSTR lpCaption, UINT uType), MessageBoxW);
Hai dòng này khai báo API sẽ bị patched cũng như prototype và calling convention của các API này.
Tạo 2 function detour sao cũng được, ví dụ thêm vào thông báo 1 dòng gì đó, để buffer là 1024 chắc cũng đủ. Lưu ý calling convention của các hàm này cũng phải tương ứng với khai báo Trampoline

int WINAPI PatchedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
 CHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfA(buffer, sizeof(buffer), "%s - patched by Detours", lpText);

 return TrampolineMessageBoxA(hWnd, buffer, lpCaption, uType);
}

int WINAPI PatchedMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) {
 WCHAR buffer[1024] = { 0x00 };
 ::StringCbPrintfW(buffer, sizeof(buffer), L"%s - patched by Detours", lpText);

 return TrampolineMessageBoxW(hWnd, buffer, lpCaption, uType);
}
Ngoài ra trong Intercept và UnIntercept sẽ thực hiện detour hay remove (Line 76, 94).

Trong source code cũng có sẵn cài đặt detour cho realloc (xem phần sau).
Hai export function là InstallHook và UninstallHook dùng ShellProc.
Sau bước này mình đã có Hook.dll với 2 export function là Install và Uninstall

Bước 4: tạo installer để cài đặt hook vào hệ thống. 

Để inject Hook.dll vào hệ thống cần tạo Installer project và dùng lại test project sửa dialog tạo 2 button install và uninstall.
// Installer.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
HINSTANCE g_hInstance = NULL;
BOOL CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

typedef void (*InstallHookFunc)();
typedef void (*UninstallHookFunc)();

InstallHookFunc g_lpInstallHook = NULL;
UninstallHookFunc g_lpUninstallHook = NULL;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPSTR lpCmdLine, int nCmdShow)
{
  // TODO: Place code here.
  ...
}

BOOL CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 WORD wId, wEvent;
 HMODULE hModule = NULL;
 HICON hIcon = NULL;

    switch (message)
    {
    case WM_INITDIALOG:  
  ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_UNINSTALL), FALSE);

  hModule = ::LoadLibraryA("Hook.dll");   
  if(hModule) {
   g_lpInstallHook = (InstallHookFunc)::GetProcAddress(hModule, "InstallHook");
   g_lpUninstallHook = (UninstallHookFunc)::GetProcAddress(hModule, "UninstallHook");    
  }
  
  hIcon = ::LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ICON));
  if(hIcon) {
   ::SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
  }

        return TRUE;

    case WM_COMMAND:
        wId    = LOWORD(wParam); 
  wEvent = HIWORD(wParam); 
  // Parse the menu selections:
  switch (wId)
  {
   case IDC_BUTTON_INSTALL:
       if(g_lpInstallHook) {
     g_lpInstallHook();
    }

    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_INSTALL), FALSE);
    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_UNINSTALL), TRUE);
    break;

   case IDC_BUTTON_UNINSTALL:
       if(g_lpUninstallHook) {
     g_lpUninstallHook();
     g_lpUninstallHook = NULL;
    }
    
    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_INSTALL), TRUE);
    ::EnableWindow(::GetDlgItem(hWnd, IDC_BUTTON_UNINSTALL), FALSE);
    break;

   case IDOK:
    if(g_lpUninstallHook) {
     g_lpUninstallHook();
     g_lpUninstallHook = NULL;
    }
    
    ::DestroyWindow(hWnd);
    break;

   case IDCANCEL:
    if(g_lpUninstallHook) {
     g_lpUninstallHook();
     g_lpUninstallHook = NULL;
    }
    
    ::DestroyWindow(hWnd);
    break;

   default:
    return ::DefWindowProc(hWnd, message, wParam, lParam);
  }
        return TRUE;

 case WM_DESTROY:
  if (g_lpUninstallHook) {
   g_lpUninstallHook();
   g_lpUninstallHook = NULL;
  }

        ::PostQuitMessage(0);
        return TRUE;

    case WM_CLOSE:
  if(g_lpUninstallHook) {
   g_lpUninstallHook();
   g_lpUninstallHook = NULL;
  }

        ::DestroyWindow(hWnd);
        return TRUE;
    }

    return FALSE;
}
Mình khai báo prototype 2 function Install và Uninstall và 2 global function và thực hiện load từ Hook.dll, thực hiện test một cách đơn giản thôi.
typedef void (*InstallHookFunc)();
typedef void (*UninstallHookFunc)();

InstallHookFunc g_lpInstallHook = NULL;
UninstallHookFunc g_lpUninstallHook = NULL;

Thực hiện load Hook.dll ngay trong WM_INITDIALOG và lấy địa chỉ hàm luôn cho tiện. Tới đây mọi việc xong xuôi, để test mình chạy installer và install hook. 

Tới đây mình lấy cái Test.exe cũ ra xài, hoặc xài bất kỳ ứng dụng nào có MessageBox
Nếu thấy nội dung của MessageBox có thay đổi là OK.

OK vậy là đã hook API MessageBox rồi.

Bước 5: patch function realloc. realloc là function của CRT. 

CRT có version đi với OS và version của Visual Studio (để VS thực hiện debug).

API realloc trong game sẽ xài CRT ship với OS, mình có thể kiểm tra bằng cách nào cũng được, dùng Command Prompt trong Visual Studio Tools rồi xuất ra file imports.txt trong C chẳng hạn, gõ
dumpbin /imports elementclient.exe > C:\imports.txt

Trong danh sách sẽ thấy realloc trong MSVCRT.DLL.

Trong khi đó code thì mình sẽ dùng MSVCR9D.DLL của Visual Studio 2008 (bản đang dùng). Mình sẽ thực hiện patch 2 version này và test thử với Test.exe cũng tạo lua_State.

Cách làm hơi khác chút xíu, đầu tiên khai báo prototype của realloc.
typedef void* (__cdecl *ReAllocFunc)(void* memptr, size_t newsize); 
Khai báo một cặp target và trampoline function
ReAllocFunc g_lpTargetReAllocMSVCRT = NULL;   // Target realloc function pointer in MSVCRT
ReAllocFunc g_lpTrampolineReAllocMSVCRT = NULL;  // Trampoline realloc function pointer in MSVCRT

ReAllocFunc g_lpTargetReAllocMSVCR90D = NULL;  // Target realloc function pointer in MSVCR90D
ReAllocFunc g_lpTrampolineReAllocMSVCR90D = NULL; // Trampoline realloc function pointer in MSVCR90D

Thực hiện tạo function detour đơn giản gọi function trampoline và log lại thông tin trả về vào debug output nếu size của realloc là 376 mà mình đã tìm ở trên.

Thêm trong Intercept và UnIntercept, mình dùng DetourFindFunction để tìm realloc, nếu tìm thấy thì thực hiện detour bằng DetourFunction
g_lpTargetReAllocMSVCRT = (ReAllocFunc)DetourFindFunction("MSVCRT.DLL", "realloc");
if(g_lpTargetReAllocMSVCRT) {
 g_lpTrampolineReAllocMSVCRT = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCRT, 
  (PBYTE)PatchedReAllocMSVCRT);
}

g_lpTargetReAllocMSVCR90D = (ReAllocFunc)DetourFindFunction("MSVCR90D.DLL", "realloc");
if(g_lpTargetReAllocMSVCR90D) {
 g_lpTrampolineReAllocMSVCR90D = (ReAllocFunc)DetourFunction((PBYTE)g_lpTargetReAllocMSVCR90D, 
  (PBYTE)PatchedReAllocMSVCR90D);
}

Trong các hàm này dùng OutputDebugString để output thông tin. Để coi được thông tin xuất bởi OutputDebugString mình dùng Dbgview có thể download tại http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx.

Đến đây chạy thử với Test, nhấn 2 lần button mình có 2 dòng debug output kết quả của patched MSVCR9D.realloc.

Tương tự vậy bật game mình cũng sẽ có được 2 kết quả tạo lua_State. Tuy nhiên không giống như test app của mình có thể nhấn nút test bất cứ lúc nào. Lua state của game chỉ thực hiện init ngay khi chạy game. Thế nên installer bắt buộc phải chạy trước tiên, thực hiện install xong xuôi rồi chúng ta mới bật game.

Chẳng hiểu sao thằng Tru Tiên này cũng debug ra đây lỗi từa lưa :D. Như vậy theo kết quả của debug viewer là mình tìm được lua_State rồi, tất nhiên đây là giá trị của lua state tại lần bật game này thôi. Nó là giá trị cấp phát động nên giá trị này chưa có ý nghĩa gì nhiều. Đến đây thì còn 1 bước nữa là tìm base address của 2 giá trị cấp phát này. Mình sẽ không tắt game client mà sẽ dùng Cheat Engine để tìm giá trị base address giống như bài trước, lưu ý giá trị hexa.


Như vậy là xong cách tìm lua_State.
Chúc mọi người cuối tuần vui vẻ

2009-09-10

Sử dụng Cheat Engine để search base address dùng cho auto play

Xem thêm  Sử dụng Detours để tìm lua_State cho AutoPlay

Có nhiều bạn hỏi mình cách viết auto. Source code và demo thì cũng đã có nhiều rồi. Nhưng sau cái vụ Tru Tiên đổi address trong version 24 thì nhiều bạn muốn tự mình chỉnh lại address cho nhanh chóng thì gặp khó khăn khi tìm lại các địa chỉ này dù rằng ai cũng biết sẽ cần dùng Cheat Engine.
Mình cũng chỉ giới thiệu sơ qua cách dùng Cheat Engine mà mình đã thực hiện cho cả những ai chưa quen cũng có thể thử được. Cách thực hiện cũng chỉ mấy bước và cũng không khó khăn gì.

Bước 1

Ví dụ bạn muốn search địa chỉ của HP, chúng ta đọc thông tin HP của nhân vật giá trị là 5111
Pic 1 thông tin HP 5111
Pic 1 thông tin HP 5111

Bước 2

Dùng cheat Engine chọn process và search giá trị này, là search lần đầu tiên "New Scan", giá trị là decimal ứng với HP, nên không check Hexa
Pic 2: search HP 5111
Pic 2: search HP 5111










Bước 3

Mình có rất nhiều kết quả search và thực hiện filter. Vào game tháo bớt đồ hay làm cách nào thay đổi lượng HP, ở đây mình còn 4715 máu nên bước filter mình chọn "Next Scan" với scan type là chính xác giá trị 4715 (tất nhiên có thể chọn scan type khác ví dụ bạn làm cách sao bị mất HP và chỉ cần chọn decrease tức giá trị HP giảm, nhưng nó lâu hơn)
Pic 3 kết quả search HP 5111

















Pic 4 tháo bớt đồ để thay đổi HP 4715



Pic 5 search HP mới 4715


Bước 4

Sau bước 3 mình còn 4 địa chỉ. Những địa chỉ này chỉ đúng trong game hiện tại đang chạy nên chúng ta không sử dụng được. Nhưng những địa chỉ này sẽ tham chiếu 1 địa chỉ không đổi theo 1 cách nào đó, gọi là base address cần tìm. Kinh nghiệm thì chúng ta nên để ý 2 địa chỉ gần nhau ở vùng chừng 0x09xxxxxx, vì mình biết 1 cái là HP và 1 cái là max HP cùng giá trị. Trước tiên 2 cái này vào khung kết quả bên dưới bằng cách double click hoặc select rồi nhấn button mũi tên nhỏ như trong hình. Nếu mang lại đồ thì max HP đổi còn HP thường ko đổi (trừ khi tự tăng HP).
Pic 6 sau khi filter còn 4 address ứng với 4715


Pic 7 thường HP và max HP gần nhau, base address ưu tiên địa chỉ nhỏ hơn, nhớ 2 địa chỉ này




Bước 5

Chọn một địa chỉ, giả sử mình chọn cái trên là HP (theo suy nghĩ thì cái dưới thường là max HP) rồi thực hiện "Find out what accesses this address" (những các gì truy cập tới địa chỉ này). Chỉ cần scan 1 cái thì cái kia tính theo offset, ở trên là 09xxxxBC tới 09xxxxD4.

Pic 8 thực hiện "Find out what accesses this address"



Sau khi có kết quả scan xong thì mình thực hiện chọn "More information" để có thêm thông tin. Mình không đi sâu vào chi tiết mấy cái thông tin này (nếu biết Assembly thì sẽ dễ hiểu ESI là gì). Mục đích là tìm được address thôi nên không cần tìm hiểu nhiều chỗ này. Hai thông tin cần quan tâm là cái dòng màu đỏ trong cửa sổ "More information" là offset 0x24C và giá trị địa chỉ cần tìm tiếp theo là 0x9A66F70

Pic 9 kết quả




Bước 6

Search địa chỉ kế tiếp. Mình sẽ check và hexa (đây là giá trị địa chỉ), và thực hiện "New Scan". Sau khi ra kết quả cũng có nhiều, mình sẽ lần lượt tìm kiếm vận may từ trên xuống, bắt đầu là 0x02DFFAC8. Sau khi lưu kết quả vào khung results thì mình lưu dạng pointer để có thể nhớ thêm thông tin offset 0x24C.

Pic 10 search tiếp address 0x9A66F70



Pic 11 lưu lại và chuyển thành dạng pointer






Công việc tiếp vẫn là scan "Find out what accesses this address". Nếu scan với pointer khi hiện dialog mình sẽ chọn "Find out what accesses this pointer". Tiếp tục lần theo đầu mối này

Pic 12




Bước 7

Cũng như bước 5 sang bước 6, tại đây mình sẽ có danh sách kết quả.

Pic 13


Đáng chú ý là có một dòng màu xanh lá trên cùng, điều đó cho biết là địa chỉ đó sẽ dẫn đến base address. Tất nhiên mình sẽ thực hiện search ưu tiên địa chỉ đó.

Pic 14 xuất hiện address xanh lá, ưu tiên lần theo address đó. Kết quả search address xanh lá.






Công việc vẫn là kiểm tra tiếp ưu tiên address có đánh dấu màu, đến lúc này cảm giác rất là may mắn và gần như 80% thành công rồi. Kết quả của lần scan tiếp vẫn ra 1 dòng xanh lá.

Pic 15 kết quả tiếp theo




Pic 16



Đến khi mình kiểm tra không còn tham chiếu offset nữa tức giá trị địa chỉ Cheat Engine bảo search tiếp bằng chính giá trị mình vừa scan thì nó là base address.

Pic 17 không còn offset nữa -> base address



Bước 8

Tạo một pointer lưu nguyên path của HP từ base address > 1C > 28 > 24C, max HP chúng ta có thể thấy trong lần search đầu 0x09A671D4 hơn HP là 0x09A671BC nên dễ dàng suy ra từ HP base address> 1C > 28 > 264.

Pic 18 save kết quả kèm các offset từ nãy giờ thành 1 kết quả từ base address tới HP


Pic 19 kết quả dạng pointer



Đến đây có thể kiểm tra bằng cách tắt game, rồi mở lại thử có OK không. Thêm nữa có thể thử trên 1 máy khác. Tương tự như vậy mình có thể search ra MP, max MP (thử theo cách so offset với HP trước như trong trường hợp max HP.

Pic 20



Các trường hợp đặc biệt khác


Bước 9

Tọa độ x, y, z thông thường kiểu int 4 bytes nhưng search hoài không ra nên mình thử search float và cũng ra kết quả khá đơn giản x = 0x00A45EB4 trong 1 bước.


Bước 10

Search những giá trị boolean. Trong auto play mình cần một giá trị gọi là current target, địa chỉ của monster. Mình có thể suy ra khi không có monster giá trị này có lẽ là 0, do đó không focus monster nào search giá trị 0, lần đầu search tất nhiên ra vài trăm ngàn đến vài trăm triệu kết quả.

Pic 21 search không target là 0

Pic 22 search 0

Có target phải khác 0 tức filter tăng

Pic 23 focus vào mob, filter sau khi focus monster giá trị phải tăng lên.

Pic 24 filter increase


Sau đó lại bỏ focus để filter exact 0 .... sau một vài lần thì chỉ còn lại vài trăm, sau khi loại bỏ những giá trị biến đổi liên tục chỉ còn lại một ít.

Pic 25


Pic 26 bỏ các giá trị thay đổi liên tục



Như trong hình mình sẽ chú ý nguyên 1 dãy address liên tiếp nhau, chắc dãy đó là address cần search 0x09A67F22 đến 0x09A67F27.

Pic 27 chú ý các địa chỉ liên tiếp nhau



Thực hiện scan như thao tác tìm HP ở trên ra kết quả offset 0xFB4 và address 0x09A66F70, quá quen thuộc.

Pic 28 kết quả scan

Ngoại lệ

Ngoại lệ khi thực hiện như trên mà vẫn không ra, rất là xui ??? Vậy có một cách khác hơi lâu nhưng đỡ bực bội hơi. Sau khi search lần đầu tiên và filter (bước 4 như trên), mình sẽ thực hiện "Pointer scan for this address", nên chọn default để tránh game process bị die giữa chừng.

Pic 29 thực hiện pointer scan


Pic 30


Bước tiếp là optimize lại vùng scan và tăng số thread lên cho nhanh, chừng 10 thread là được. Sau đó leo lên giường ngủ chờ đến sáng hẵng tính. Sáng dậy nếu chạy xong hoặc cảm thấy thông báo số static base khá khẩm (vài trăm ngàn) thì stop cũng được.

Pic 31


Pic 32



Bước quan trọng tiếp là save lại kết quả 1 đêm này.

Sau đó tắt game, lặp lại bước 1-4 để tìm address thể hiện HP mới, giá trị này không còn như cũ nữa. Rồi chúng ta sẽ lọc trong đám static base chọn rescan đến address mới có thì kết quả sẽ được filter lại.

Pic 33


Double click sẽ cho vào trong results, lại tắt game bật lại thì một số giá trị trong results không đúng mình sẽ bỏ đi. Cuối cùng sẽ ra base address. Dễ dàng hơn nhưng cũng không phải lúc nào cũng được. Risk cũng nhiều ví dụ máy bị sao khi mình đang ngủ. Như mình có tối con mèo làm tắt máy do để cắm điện ẩu.

Còn một phần nữa sẽ nói để hoàn thiện nhưng phải debug và cũng không dùng Cheat Engine nên sẽ nói sau (trường hợp search base address của lua state, cái này thằng nhóc em search bằng VS với mode debug nhưng giờ thì không dùng cách đó được nữa). Dù gì giờ cũng khuya rồi, chúc mọi người ngủ ngon.

2009-04-26

"Từng qua tuổi 20" hay "Twentysomething: The quarter-life Crisis of Jack Lancaster"

Bắt đầu chuyển sang ngôi nhà mới.

Vậy là đã viết xong auto cho Tru Tiên online, có lẽ là OK nhất so với những auto đang có trên net, dùng được cả auto pháp bảo trong game mà chẳng có cái auto đang có nào làm được… Dù là viết để dùng personal vẫn cảm thấy vui hehe khi làm được gì đó trọn vẹn. Nhưng tự nhiên viết xong 1 thời gian rồi chẳng muốn làm gì cả. Dạo này cứ về nhà là mình lại lăn ra ngủ… chẳng hiểu. Giờ lên cty chỉ có ngáp và trông tới giờ đi về. Project thì member training chưa đủ để làm theo instructions của mình, thời gian training thì không có, mình thì lại chẳng muốn cứ lại đâm đầu vào code. Bao nhiêu năm để code những thứ củ chuối có lẽ đủ rồi, không nên cố gắng thêm nữa. Để status code for fun, không fun thì ngồi ngáp tạm vậy.

Có lẽ mình bị khủng hoảng thật rồi. Mới vừa đọc xong cuốn “Từng qua tuổi 20″ tên tiếng Anh là “Twentysomething: The quarter-life Crisis of Jack Lancaster”. Mình chẳng khùng như thằng Jack trong truyện, không béo cũng không hói đầu nhưng chắc chắn là cũng có vấn đề, năm nay 26 tuổi, sao nó trôi qua lâu quá. Ai rảnh thì đọc cuốn này thử, hơi khùng nhưng cũng có gì đó đáng đọc.

Hôm nay ngồi cài lại mấy cái máy trong nhà… mệt mỏi bây giờ đã 4h ngày Chủ nhật. Chắc mai lại nằm ngủ nữa… Mình đang chọn một cái blog mới để chuyển qua… Opera hay WordPress hay Facebook, chưa biết… Qua blog mới đã than thở rồi. Hôm nay giật mình đã gần hết tháng 4. Tự nhiên nhớ ra mình về VN đã lâu rồi mà chẳng liên lạc với ai. Còn 1 số đồ chưa gửi. Còn một gói quà sinh nhật của một cô bạn từ 1-4 năm ngoái :frown: . Còn hứa với Gam mercury đi uống cafe… từ hồi bên Nhật. Tự nhiên thấy mình chỉ lên công ty ngáp rồi về. Mình cũng chẳng đủ tiền để có thể nghỉ chừng 1/2 năm để đi lang thang. Tệ thật…

Bây giờ viết gì… Chắc sẽ bắt chước blog kỹ thuật kiểu happy programming. Cho nó đa phong cách, than thở hoài cũng chán. Lỡ đâu có thể giúp bạn bè nếu cần khỏi mất công search.

Làm cái gì? Mình bắt đầu viết auto. Một trong những ví dụ đầu tiên đụng phải là… để xem àh khi làm auto mình cần một cái progress bar có text. Search 1 vòng trên web chẳng thấy có cái nào OK cả. Hoặc có thì là progress bar viết lại từ đầu. Phát hiện thằng ProgressBar của Windows hình như không implement hàm Paint như các user control khác dù rằng nó kết thừa từ user control. Nếu override thằng OnPaint thì kết quả draw cũng như không vì sẽ bị clear kết quả draw sau khi thực hiện set giá trị. Cuối cùng mình override hàm WndProc của nó, thực hiện paint sau khi nó paint. Tức kiểm tra message gửi là paint thì thực hiện paint tiếp tại đây. Kết quả rất OK và ngắn gọn.

Hay khi bắt đầu viết auto thì phải làm gì, mình viết 1 bài về sử dụng CheatEngine, để trước chứ chưa viết hoàn chỉnh từ từ sẽ update
Sử dụng Cheat Engine để search base address dùng cho AutoPlay
Sử dụng Detours để tìm lua_State cho AutoPlay

Tự nhiên đến đây lại không biết cách post source code làm sao cho đẹp. Mình cũng không biết cách nào. Đơn giản có thể nghĩ là copy vào Word, Save as HTML rồi view source copy wa. Được không ta? Nhưng chắc cũng không giống trong Visual Studio khi dùng Resharp hay CodeSMART với Visual AssistX. Lại phải lên net search. Hic nếu 1 ngày không có google thì chắc mình không làm việc được…

Mình search ra một trang format code http://manoli.net/csharpformat/ nhưng dùng CSS không biết có paste vào blog được không. Cuối cùng phát hiện cái blog WordPress không cho dùng CSS mà phải dùng tag của nó, chỉ có điều source có comment bị format sai từa lưa http://support.wordpress.com/code/. Để sau này mình sẽ tìm cách khác. Giờ đi ngủ thôi…