2010年12月28日 星期二

Tree traversal

對於一顆在tree上的node而言, 不管他有多少個children, 我們總會希望用一些方法來取得或是找到他底下所有的node, 這時traversal的方法就出現了, 但是在一個binary tree裡我們常看到pre-order, in-order及post-order, 這些方法我們都學過, 一但到了一個node有多個children時怎麼辦呢?

這個問題源自於工作上的需求, 這個tree還是一個可以回溯找parent的, 也就是他是一個double linked tree, 雖然這link的方法無礙於我們訪問這個tree, 但有時遇到要訪問同一層level的node時,這時非常的好用...

演算法如下,

void InOrder(TreeNode* pNode) {
    if(pNode != NULL) {
        InOrder(pNode->left);
        cout << pNode->data;
        InOrder(pNode->right);
    }
}

void PreOrder(TreeNode* pNode) {
    if(pNode != NULL) {
        cout << pNode->data;
        PreOrder(pNode->left);
        PreOrder(pNode->right);
    }
}

void PostOrder(TreeNode* pNode) {
    if(pNode != NULL) {
        PostOrder(pNode->left);
        PostOrder(pNode->right);
        cout << pNode->data;
    }
}

void LevelOrder(TreeNode* pNode) {
    if(pNode != NULL) {
        TreeNode* pTmpNode = pNode;
        AddToQueue(pTmpNode);
        while(pTmpNode != NULL) {
            pTmp = DeQueue();
            cout << pTmpNode->data;
            AddToQueue(pTmpNode->left);
            AddToQueue(pTmpNode->right);
        }
    }
}

這樣一來就完成了, 當然如果一個node不只有left, right兩個children, 那就自己implement一個get next child吧, 如此一來, 就完成了...

2010年12月21日 星期二

ノルウェイの森

這本小說出來23年了, 早在14年前高一時, 就看過這本小說, 當時看不懂, 後來再看一次是在書店裡, 雖然懂了, 但其實沒有很深入的去了解作者想表達的是什麼。

直到上星期, 這部小說搬上了大螢幕, 很多人總認為這部電影拍的沒有小說寫得好...雖然我還沒看過電影, 但我想我能理解為何一堆人都說拍的不好, 或是看不懂...

 

其實要理解一本小說最好的方法, 就是去理解作者寫小說的那個年代, 到底發生了什麼大事, 小說裡的年代是被設置在1960年代晚期, 那時日本剛剛戰敗, 當時日本各大學罷課頻頻發生, 很像早一點中國發生的學運, 大學生不唸書, 一天到晚搞活動, 而主角和第一位女主角都不喜歡這樣子的活動, 所以他們孤立於人群之外。當然這部小說是主角的回憶, 所以有時在看時, 會覺得不太連續, 而這也是作者希望讀者多看幾次的關係, 才會用這樣的手法寫小說, 因為插述寫法的小說, 沒有一定功力, 是寫不出來的...

 

書裡面談到很多的生死(主角的朋友, 以及直子都是自殺身亡), 直到後來小綠用開心活潑感動了主角, 其實在面對人生時, 很多人會選擇像是直子的憂鬱來面對, 而不是像小綠一樣的樂觀活潑, 就算小綠和家人的關係再不好, 在小綠的父母重病時, 他還是一樣照顧著他們, 很多人說這一點是矛盾的, 我倒覺得小綠是唯一看懂人生的人..

 

每每談到自殺, 書裡自殺的人物還真是不少, 但這也是作者想強調的, 在那個年代裡, 人們的心理是多麼的不健全, 傳統到全新的流行, 到處充滿著矛盾, 那時沒有人知道什麼是未來, 未來有什麼, 曾有很多人和我說, 這本小說裡充滿了性愛的場景, 其實這些人還是不懂作者想表達的是什麼...

 

感情, 性, 生死..正是作者認為人生最快樂及最苦惱的事, 全是因為這些, 所以作者才會寫下"死並不是終結生的決定性要素。在那裏死只不過是構成生的許多要素之一。"這不正是作者最深刻的體會嗎?

 

"沒有什麼人喜歡孤獨的,只是不勉強交朋友而已,因為就算那樣做也只有失望而已。"

為什麼會說出這句話, 那不正是時下年輕人最常做的一件事, 交多一點朋友來填滿自己人生的空白, 後來才發現原來連自已都不認識, 認識到的只是一群能填滿你時間的朋友, 而不是填滿你的人生, 讓你的人生變得更有義!!

 

我最記得的一幕是小綠說把蛋糕丟到窗外的事, 為什麼會這麼清楚, 因為人, 常有不同的想法, 很有可能前一秒就和下一秒完全不同, 而這也是人生最重要的轉折處, 你說小綠說出這些話來之前, 他心裡想的是什麼呢?人生常會因為一個忽然來的意外而有所改變, 小綠明白, 但看書的人又有多少人懂呢?

 

電影拍得沒有小說好, 應該說村上大師寫作的手法很難讓電影能夠比他好, 所以看完電影不懂的人, 回過頭再看看小說吧, 也許這本書年代久遠, 跟不上潮流了, 但回過頭去看看那些年代的人, 其實現代的人並不見得活得比他們快樂...

 

2010年12月6日 星期一

用OO思考加減乘除的作法!?

在OO的世界裡, 什麼都能用Object來實現, 但也許會有人說, 下面的程式很好

 

int main() {
    int a = 0, b = 0;
    char op = 0;
    cout << "Please input the operation: +, -, *, /" << endl;
    cin >> op;
    cout << "Please input the first number ==> ";
    cin >> a;
    cout << "Please input the second number ==>";
    cin >> b;

    int result = 0;
    if(op == '+') {
        result = a+b;
    } else if(op == '-') {
        result = a-b;
    } else if(op == '*') {
        result = a*b;
    } else if(op == '/') {
        result = a/b;
    } else {
        // error!!
    }
    cout << "Result: " << result << endl;
}

很好, 這程式其實沒什麼大問題, 而且寫得很清楚(因為是我寫的!!XD), 但..沒有錯的程式就代表是好程式嗎?在以前可能是, 因為這code好懂又沒錯, 直到OO世界之後, 這樣的code不但不好, 而且充滿了不可知的後果!!為什麼說不可知呢??如

試想一下, 這時只果我想再加入一個operation時怎麼做, 就叫做%好了, 取出果個除數的餘數, 這時要改動的code有多少呢?

想一下好像又不難, 在if else再加一個就好了不是嗎??對, 這是其中之一的解答, 但確不是最好的解答..如果你是一個OOP的程式設計師, 你肯定馬上就發現這樣的修改太麻煩了..所以你會這麼寫..

class IOperation abstract {
public:
    virtual double GetResult() = 0;
    void SetNum1(double num1) {
    m_dNum1 = num1;
}
    void SetNum2(double num2) {
    m_dNum2 = num2;
}
protected:
    double m_dResult;
    double m_dNum1;
    double m_dNum2;
};

寫到此, 大概也就知道+有自己的兩個元素, -也有, /也有, *也有, 所以要再加一個%也不是什麼難事, 只要繼承就好, 所以, 再簡單的程式, 也有可以使用OOD的地方, 別再小看OO了, 另外這個方法, 其實也是design pattern裡的工廠模式, 只要有一個factory, 就可以create出任何的+-*/方法, 這樣一來對於client, 他只要input 三個東西, 就可以得到結果, 不用去考慮裡面的實作..也算是一種很好的應用...

2010年11月28日 星期日

在c/c++裡盡可能的使用forward declaration

這個方法是讓你不要在.h檔裡include了一堆其他的.h檔, 好處是當其.h修改時, 並不會影響到太多的c.pp須要重新compile, 某方面也加快了build project的速度, 其最大的好處是, .h檔不會太雜亂.

作法如下(參考http://www-subatech.in2p3.fr/~photons/subatech/soft/carnac/CPP-INC-1.shtml), 其實大家都知道, 但很少人做到..

Suppose you want to define a new class B that uses objects of class A.

  1. B only uses references or pointers to A. Use forward declaration then : you don't need to include <A.h>. This will in turn speed a little bit the compilation.
      class A ;
      
      class B {
        private:
          A* fPtrA ;
        public:
          void mymethod(const& A) const ;
      } ;
      
  2. B derives from A or B explicitely (or implicitely) uses objects of class A. You then need to include <A.h>
      #include <A.h>
      
      class B : public A {
      
      } ;
      
      class C {
        private:
          A fA ;
        public:
          void mymethod(A par) ;   
      }

 

盡可能的使用forward declaration

這是個原則性問題, 但通常我們不會寫, 我們會在一個.h檔裡include其他的.h檔, 這是不好的作法, 因為你很有可能改了一個.h檔之後, 造成很多的.cpp須要重build...

所以我們可以怎麼做呢?

 

2010年11月26日 星期五

WaitForSingleObject卻無法跳出!?

這是一個很好的sample code

 

UINT fnThread0(LPVOID p)

{

ThreadPara* pPara = (ThreadPara*)p;

while (pPara->bStart)

{

clock_t c1 = clock();

if ( c1 % 1000 < 250 * 1 )

pPara->pDlg->GetDlgItem(IDC_TEXT1)->SetWindowText("\\");

else if ( c1 % 1000 < 250 * 2 )

pPara->pDlg->GetDlgItem(IDC_TEXT1)->SetWindowText("|");

else if ( c1 % 1000 < 250 * 3 )

pPara->pDlg->GetDlgItem(IDC_TEXT1)->SetWindowText("/");

else 

pPara->pDlg->GetDlgItem(IDC_TEXT1)->SetWindowText("--");

 

Sleep(250);

}

pPara->pDlg->GetDlgItem(IDC_TEXT1)->SetWindowText("Stop");

return 1000;

}

 

void CThreadDlg::OnBnClickedOk()

{

m_ThreadPara.pDlg = this;

if ( m_ThreadPara.bStart == false )

{

// begin thread

m_ThreadPara.bStart = true;

m_pThreadHandle = AfxBeginThread( fnThread0, &m_ThreadPara );

}

else

{

// close thread

m_ThreadPara.bStart = false;

        Sleep(1000);

::WaitForSingleObject( m_pThreadHandle->m_hThread, INFINITE );

}

Sleep(0);

}

這段code哪裡出了問題呢?其實大家會認為我設了flag而且有等這個thread結束就沒問題了, 但是請看下面的圖



所以, 在設定falsg為false之後, 立即進到wait for single object, 而這時worker thread還沒有完成, 所以進到了更新UI的動作, 但main thread(UI thread)已被WaitForSingleObject block住了, 所以此時worker thread也在等main thread, 於是乎兩個互等, 造成的dead lock...

如何避開呢?其實大家如果記得windows API裡還有一個叫做PostMessage的東西, 建一個自己的message type, 然後寫一個function去處理吧, 這樣一來就不會有這個情形發生...

感謝Jame提供的sample code..這是一個非常好的例子...

 

2010年11月23日 星期二

不用使用任何額外的記憶體, 就把字串反轉!!

字串反轉, 這是個老問題, 隨手寫來也是很快

char* t = "123456789\0";

char* l = new char[strlen(t)];

for(size_t i = 0; i < strlen(t); ++i) {

    l[strlen(t)-1-i] = t[i]; // copy t to l

}

 

沒啥大問題, 但..有沒有辦法不使用額外的記憶體就把字串copy完呢?看下面的程式

 

    char str[10] = {'1', '2', '3', '4', '5', '6', '7'};

    char *a, *b, c;

    a = b = str;

    while (*b) 

        b++; 

    --b;

    while (b >a) {

        c = *a;

        *a++ = *b;

        *b-- = c;

    }

這樣不是用了兩個pointer及一個c嗎?其實在打開compiler最佳化後, 來看一下組合語言

int _tmain(int argc, _TCHAR* argv[])

{

00401000  sub         esp,10h 

00401003  mov         eax,dword ptr [___security_cookie (403000h)] 

00401008  xor         eax,esp 

0040100A  mov         dword ptr [esp+0Ch],eax 

    char str[10] = {'1', '2', '3', '4', '5', '6', '7'};

0040100E  xor         eax,eax 

00401010  mov         dl,31h 

    char *f, *t, c;    /// 這裡並沒有allocate任何記憶體

    f = t = str;

    while (*t) 

00401012  test        dl,dl 

00401014  mov         word ptr [esp+7],ax 

00401019  mov         byte ptr [esp+9],al 

0040101D  lea         eax,[esp] 

00401020  mov         byte ptr [esp],dl 

00401023  mov         byte ptr [esp+1],32h 

00401028  mov         byte ptr [esp+2],33h 

0040102D  mov         byte ptr [esp+3],34h 

00401032  mov         byte ptr [esp+4],35h 

00401037  mov         byte ptr [esp+5],36h 

0040103C  mov         byte ptr [esp+6],37h 

00401041  mov         ecx,eax 

00401043  je          wmain+4Dh (40104Dh) 

        t++;              //使用t時是直接使用eax

00401045  add         eax,1 

00401048  cmp         byte ptr [eax],0 

0040104B  jne         wmain+45h (401045h) 

    --t;

0040104D  sub         eax,1 

 

    while (t > f) {

00401050  lea         edx,[esp] 

00401053  cmp         eax,edx 

00401055  jbe         wmain+6Bh (40106Bh) 

00401057  push        ebx  

        c = *f;

        *f++ = *t;       /// 使用f時是使用ebx

00401058  mov         bl,byte ptr [eax] 

0040105A  mov         dl,byte ptr [ecx] 

0040105C  mov         byte ptr [ecx],bl 

        *t-- = c;         /// 使用c時是使用ecx

0040105E  mov         byte ptr [eax],dl 

00401060  add         ecx,1 

00401063  sub         eax,1 

00401066  cmp         eax,ecx 

00401068  ja          wmain+58h (401058h) 

0040106A  pop         ebx  

    }

return 0;

}

 

所以..完全沒用到記憶體耶..XD

2010年11月22日 星期一

利用boost shared_mutex來實作read / write lock

 

How to implement a mulltiple read single write locker? This is the sample code by using boost shared_mutex. The other classes are using shared_lock, upgrade_lock and upgrade_to_unique_lock.

 

using namespace boost;


typedef boost::shared_mutex rwmutex;

typedef boost::shared_lock<rwmutex> readLock; 

typedef boost::upgrade_lock<rwmutex> upgradeLock;

typedef boost::upgrade_to_unique_lock<rwmutex> writeLock;

 

 

rwmutex  _rwmutex; 

 void readOnly() {

    {

        readLock rdlock(_rwmutex);

        ... do something ... 

    }

 

void writeOnly() {

    {
        upgradeLock lock(_rwmutex);

        writeLock wLock(lock);

        ... do something

    }

 

2010年9月18日 星期六

[Java] 貪食蛇

這是一個非常簡單的畫出移動及按鍵的控制..

import java.awt.*;

import java.awt.event.*;

import java.util.*;

import javax.swing.*;


public class Snake extends JFrame implements Runnable {

  private PaintPanel paintPanel = new PaintPanel();

  private boolean isRunning = true;

  private long[] sleepTime  = {500, 400, 300, 200, 100};

  private int dir = KeyEvent.VK_RIGHT;

  static Snake s = null;

  private LinkedList<Point> snakeList = new LinkedList<Point>();

  private int resolution = 20;

  public Snake() {

    super("Java Snake");

    setSize(500, 500);

    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();

    setLocation(((int)dim.getWidth() - 500) / 2, ((int)dim.getHeight() - 500) / 2);

    initComponent();

    initSnake();

    setVisible(true);

    setDefaultCloseOperation(EXIT_ON_CLOSE);

  }

  private void initSnake() {

    snakeList.add(new Point(7,0));

    snakeList.add(new Point(6,0));

    snakeList.add(new Point(5,0));

    snakeList.add(new Point(4,0));

    snakeList.add(new Point(3,0));

    snakeList.add(new Point(2,0));

    snakeList.add(new Point(1,0));

    snakeList.add(new Point(0,0));

  }

  private void initComponent() {

    setLayout(new BorderLayout());

    add(paintPanel);

    addKeyListener(new SnakeKeyListener());

    addComponentListener(new ComponentListener() {

      public void componentHidden(ComponentEvent arg0) {

      }

      public void componentMoved(ComponentEvent arg0) {

      }

      public void componentResized(ComponentEvent arg0) {

        Dimension d = s.getSize();

        if(d.width <= 500) s.setSize(500, 500);

        if(d.height <= 500) s.setSize(500, 500);

      }

      public void componentShown(ComponentEvent arg0) {

      }

    });


  }

  public void run() {

    while(isRunning) {

      try {

        Thread.sleep(sleepTime[0]);

      } catch (InterruptedException e) {

        e.printStackTrace();

      }

      checkBoundary();

      moveSnake();

      paintPanel.repaint();

    }

  }

  private void moveSnake() {

    Point p = snakeList.get(0);

    Point newPoint = null;

    switch(dir) {

      case KeyEvent.VK_RIGHT:

        newPoint = new Point(p.x + 1, p.y);

        snakeList.addFirst(newPoint);

        break;

      case KeyEvent.VK_LEFT:

        newPoint = new Point(p.x - 1, p.y);

        snakeList.addFirst(newPoint);

        break;

      case KeyEvent.VK_UP:

        newPoint = new Point(p.x, p.y - 1);

        snakeList.addFirst(newPoint);

        break;

      case KeyEvent.VK_DOWN:

        newPoint = new Point(p.x, p.y + 1);

        snakeList.addFirst(newPoint);

        break;

    }

    snakeList.removeLast();

  }

  private void checkBoundary() {

    

  }

  public static void main(String[] args) {

      s = new Snake();

    Thread d = new Thread(s);

    d.start();


  }

  private final class SnakeKeyListener extends KeyAdapter {

    public void keyPressed(KeyEvent arg0) {

      int keyDir = arg0.getKeyCode();

      if(keyDir != KeyEvent.VK_UP && keyDir != KeyEvent.VK_DOWN &&

          keyDir != KeyEvent.VK_RIGHT && keyDir != KeyEvent.VK_LEFT) {

        return;

      }

      if((dir == KeyEvent.VK_UP && keyDir == KeyEvent.VK_DOWN) || 

         (dir == KeyEvent.VK_DOWN && keyDir == KeyEvent.VK_UP) ||

         (dir == KeyEvent.VK_RIGHT && keyDir == KeyEvent.VK_LEFT) ||

         (dir == KeyEvent.VK_LEFT && keyDir == KeyEvent.VK_RIGHT)) {

        System.out.println("return");

        return;

      }

      dir = keyDir;

    }

  }

  private final class PaintPanel extends JPanel {


    public PaintPanel() {

      setBackground(Color.DARK_GRAY);

    }

    public void paint(Graphics g) {

      CleanBackground(g);

      PaintSnake(g);

    }

    private void PaintSnake(Graphics g) {

      Dimension d = null;

      d = getSize(d);

      for(int i = 0; i < snakeList.size(); ++i) {

        g.setColor(new Color(0, 255 - i*(255 / (snakeList.size())), 0));

        Point p = snakeList.get(i);

        g.fill3DRect(

            p.x*(int)(d.width / resolution), 

            p.y*(int)(d.height / resolution), 

            d.width / resolution, d.height / resolution, 

            true

            );

      }

    }

    private void CleanBackground(Graphics g) {

      Dimension d = null;

      d = getSize(d);

      g.setColor(Color.RED);

      g.fillRect(0, 0, d.width, d.height);

      g.setColor(Color.DARK_GRAY);

      for(int i = 0; i < d.width / resolution; ++i) {

        for(int j = 0; j < d.height / resolution; ++j){

          g.fill3DRect(

              i*(int)(d.width / resolution), 

              j*(int)(d.height / resolution), 

              d.width / resolution, 

              d.height / resolution, 

              true);

        }

      }

    }

  }

}

2010年9月15日 星期三

[C++ template]有趣的template, 常被誤解的型別

通常我們要做==比較時, 會希望兩邊的型別是相同的, 如果是base type如int, double, char, 這些會自動轉型, 但是遇到class或是template時怎麼辦, 其實可以在一個class裡實作出一個template的operation overload就ok了..範例如下...

下面的例子可能比較複雜一點,  但其實也只是在一個template class裡再做另一個template member function而已...

template<typename T>

class MyType {

public:

    MyType(T value) {

        m_value = value;

    };

    T GetValue() {

        return m_value;

    };

    template<typename T2> bool operator==(T2 AnotherType) {

        if(this->GetValue() == AnotherType.GetValue()) {

            return true;

        }

        return false;

    };

public:

    T m_value;};

int main() {

    MyType<int> myInt(5);

    MyType<double> myDouble(5.0);

    if(myInt == myDouble) {

        cout << "Yes!!" << endl;

    }

    return 0;

}

-----

所謂的template型別(這裡指的是被指定後的型別), 應該這麼考慮的..

"MyType<int>"是一種型別..

"MyType<double>"亦是另一種型別

 

試想在所有的程式裡..宣告一個變數一定是 "型別名" "變數名"

所以上面兩種是不同型別, 另外在偏特化的例子中, 

SomeType<int, 6>

SomeType<int, 5>

這兩個是不同型別, 寫個程式來表達一下好了..

 

-----美麗的分隔線------

template <typename T>

inline T cost& max(T const& a, T const& b) {

    return a < b ? b : a;

}

 

int main() {

    std::string s = "apple";

    ::max("123456", "567890");    // OK!

    ::max("12345", "123456");      // ERROR!!

    ::max("apple", s);                   // ERROR!!

    return 0;

}

為什麼不行呢, 因為型別不同, 很多人可能會誤以為不都是字串嗎, 特別是第二個...

真是不好意思啊, 在compiler的眼裡,

"12345"和"123456"的型別, 分別是char[5]及char[6]

所以理所當然, 第三個max也不行了..一個是char[5], 一個是string...

要小心啊..型別的問題....

2010年7月22日 星期四

.lnk virus

Currently, this is a 0 day vulnerability of Microsoft. You can not avoid this.
Some days ago, MS had done this 0 day vulnerability. Please update your windows.

2010年6月23日 星期三

Get Icon and save it into a png file

This is not a news to get a icon from a file. But how about the process? I can extrace the process image file name and use it to extract the icons that it contains. The output is a handle of HICON. I survey many articles to find out the solution. Finally, I found a solution to the saving issue. The following sample codes will show the GDI+ and save the HICON into a PNG file. Of course, you can save it into other format that GDI+ provided.

 

HICON GetFileIcon(const CString& strFileName, CString& strSaveFileName, bool bSmallIcon) {

    SHFILEINFO sfi;

    SHGetFileInfo((LPCTSTR)strFileName,

        FILE_ATTRIBUTE_NORMAL,

        &sfi,

        sizeof(SHFILEINFO),

        SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | (bSmallIcon ? SHGFI_SMALLICON : SHGFI_LARGEICON));


    if(sfi.hIcon) {

        Bitmap* pBitmap = Bitmap::FromHICON(sfi.hIcon);

        CLSID encoderClsid;

        GetEncoderClsid(L"image/png", &encoderClsid);

        CStringW strPngFileName;

#ifndef _UNICODE

        strPngFileName.Format(L"%S", strFileName.GetString());

#else

        strPngFileName = strFileName;

#endif

        int nStart = strPngFileName.ReverseFind('\\') + 1;

        int nEnd = strPngFileName.ReverseFind('.');

        int nLength = nEnd - nStart;

        strPngFileName = strPngFileName.Mid(nStart, nLength);

        strPngFileName = CStringW(L"C:\\") + strPngFileName + (bSmallIcon ? CStringW("Small.png") : CStringW("Large.png"));

        strSaveFileName = strPngFileName;

        pBitmap->Save(strPngFileName.GetString(), &encoderClsid, NULL);

    }

    return sfi.hIcon;

}

 

You should include the header file <gdiplus.h> and use the name space "GdiPlus".

2010年6月7日 星期一

Memory Scan Tool

This will not too complex. It a easy way to let you scan your process memory like some game cheater. Of course, there is a free tool named "Game Cheater". That is open source one.

But I just want to write a "string" scan for myself. You can modify this by your self. Totally free.

See the definition.

 

#ifndef __MEMORY_SCAN_TOOL_H__

#define __MEMORY_SCAN_TOOL_H__

 

#pragma once

 

#include <vector>

 

typedef struct _MEMBLOCK {

    DWORD pid;                      // process id

    HANDLE hProc;                   // handle of the process

    unsigned char * addr;           // block address (start address)

    SIZE_T size;                    // memory block size

    unsigned char * buffer;         // a copy buffer of this memory block

    unsigned char * searchmask;     // used for searching

    SIZE_T matches;                 // used for searching

    SIZE_T data_size;               // data size when search

    struct _MEMBLOCK * next;        // linked list

} MEMBLOCK, *PMEMBLOCK;

 

typedef struct _FOUNDADDR {

    unsigned char * addr;           // base address

    unsigned long offset;           // off set

} FOUNDADDR;

 

//#define IS_IN_SEARCH(mb, offset) (mb->searchmask[(offset)/8] & (1 << ((offset)%8)))

//#define REMOVE_FROM_SEARCH(mb, offset) mb->searchmask[(offset)/8] &= ~(1 << ((offset)%8));

 

///

// This class is used for memory scan

// 1. Get memory block for scaning

// 2. Get the scan result

///

class CMemoryScanTool

{

public:

    CMemoryScanTool();

    ~CMemoryScanTool();

 

    static CMemoryScanTool* GetMemoryScanToolInstance();

    static void ReleaseMemoryScanToolInstance();

 

    // create the memory list by using CreateMemoryBlock

    PMEMBLOCK CreateMemoryList(unsigned int pid, int data_size);

    // create the memory block

    PMEMBLOCK CreateMemoryBlock(HANDLE hProc, MEMORY_BASIC_INFORMATION* meminfo, int data_size);

    // free the memory block (one block with buffer)

    void FreeMemoryBlock(PMEMBLOCK mb);

    // free the memory block list (by using FreeMemoryBlock)

    void FreeMemoryBlockList(PMEMBLOCK mb_list);

    // update the memory block data by using ReadProcessMemory

    void UpdateMemoryBlock(PMEMBLOCK mb);

    // Dump the information into a file

    void DumpScanInformation(FILE* f, PMEMBLOCK mb_list);

    void DumpScanInformation(char* szFileName, PMEMBLOCK mb_list);

 

    // scan the input string in the memory block list

    // Return Value: true indicate that the input string is in the memory block, the nAddr indicate the offset of this block

    bool ScanString(const wchar_t* szInput, PMEMBLOCK mb, unsigned long& nAddr);

    bool ScanString(const char* szInput, PMEMBLOCK mb, unsigned long& nAddr);

 

    // scan all memory block by using ScanString

    bool ScanAllBlock(const wchar_t* szInput, PMEMBLOCK mb_list, std::vector<FOUNDADDR>& vecOut);

    bool ScanAllBlock(const char* szInput, PMEMBLOCK mb_list, std::vector<FOUNDADDR>& vecOut);

 

};

 

#endif __MEMORY_SCAN_TOOL_H__

 


 

See the implementation.

 

#include "StdAfx.h"

#include "MemoryScanTool.h"

 

using namespace std;

 

namespace {

    static CMemoryScanTool* g_pMemoryScanTool = NULL;

    typedef LONG  NTSTATUS;

    typedef NTSTATUS (WINAPI *pFnRtlAdjustPrivilege)(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled);

    pFnRtlAdjustPrivilege  RtlAdjustPrivilege;

}

 

CMemoryScanTool::CMemoryScanTool()

{

    RtlAdjustPrivilege = (pFnRtlAdjustPrivilege)GetProcAddress(LoadLibrary(_T("Ntdll.dll")), "RtlAdjustPrivilege");

    BOOLEAN bEnabled = FALSE;

    // get debug privilege

    RtlAdjustPrivilege(20, 1, 0, &bEnabled);;

}

 

CMemoryScanTool::~CMemoryScanTool()

{

}

 

CMemoryScanTool* CMemoryScanTool::GetMemoryScanToolInstance() {

    if(g_pMemoryScanTool == NULL)

        g_pMemoryScanTool = new CMemoryScanTool();

    return g_pMemoryScanTool;

}

 

void CMemoryScanTool::ReleaseMemoryScanToolInstance() {

    if(g_pMemoryScanTool)

        delete g_pMemoryScanTool;

    g_pMemoryScanTool = NULL;

}

 

PMEMBLOCK CMemoryScanTool::CreateMemoryList(unsigned int pid, int data_size) {

    MEMBLOCK* mb_list = NULL;

    MEMORY_BASIC_INFORMATION meminfo;

    unsigned char* addr = 0;

    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

    if(hProc) {

        while(true) {

            if(VirtualQueryEx(hProc, addr, &meminfo, sizeof(meminfo)) == 0)

                break;

#define WRITABLE (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)

            if((meminfo.State & MEM_COMMIT) && (meminfo.Protect & WRITABLE)) {

                MEMBLOCK* mb = CreateMemoryBlock(hProc, &meminfo, data_size);

                if(mb) {

                    UpdateMemoryBlock(mb);

                    mb->pid = pid;

                    mb->next = mb_list;

                    mb_list = mb;

                }

            }

            addr = (unsigned char*)meminfo.BaseAddress + meminfo.RegionSize;

        }

    }

    return mb_list;

}

 

PMEMBLOCK CMemoryScanTool::CreateMemoryBlock(HANDLE hProc, MEMORY_BASIC_INFORMATION* meminfo, int data_size) {

    PMEMBLOCK mb = (PMEMBLOCK)malloc(sizeof(MEMBLOCK));

    if(mb) {

        mb->hProc = hProc;

        mb->addr = (unsigned char*)meminfo->BaseAddress;

        mb->size = meminfo->RegionSize;

        mb->buffer = (unsigned char*)malloc(meminfo->RegionSize);

        mb->searchmask = (unsigned char*)malloc(meminfo->RegionSize/8);

        memset(mb->searchmask, 0xff, meminfo->RegionSize/8);    // mark the byte is in searching.

        mb->matches = meminfo->RegionSize;

        mb->data_size = data_size;

        mb->next = NULL;

    }

    return mb;

}

 

void CMemoryScanTool::FreeMemoryBlock(PMEMBLOCK mb) {

    if(mb) {

        if(mb->buffer)

            free(mb->buffer);

 

        if(mb->searchmask)

            free(mb->searchmask);

 

        free(mb);

    }

}

 

void CMemoryScanTool::FreeMemoryBlockList(PMEMBLOCK mb_list) {

    CloseHandle(mb_list->hProc);

    while(mb_list) {

        PMEMBLOCK mb = mb_list;

        mb_list = mb_list->next;

        FreeMemoryBlock(mb);

    }

}

 

// Read the memory data from a process id in MEMBLOCK

void CMemoryScanTool::UpdateMemoryBlock(PMEMBLOCK mb)

{

    static unsigned char tmpbuf[128*1024];

    SIZE_T bytes_left = 0;

    SIZE_T total_read = 0;

    SIZE_T bytes_to_read = 0;

    SIZE_T bytes_read = 0;

 

    if(mb->matches > 0) {

        bytes_left = mb->size;

        mb->matches = 0;

        while(bytes_left) {

            bytes_to_read = (bytes_left > sizeof(tmpbuf)) ? sizeof(tmpbuf) : bytes_left;

            ReadProcessMemory(mb->hProc, mb->addr + total_read, tmpbuf, bytes_to_read, (SIZE_T *)&bytes_read);

            if(bytes_read != bytes_to_read) break;

            memcpy(mb->buffer + total_read, tmpbuf, bytes_read);

            bytes_left -= bytes_read;

            total_read += bytes_read;

        }

        mb->size = total_read;

    }

}

 

 

void CMemoryScanTool::DumpScanInformation(FILE* f, PMEMBLOCK mb_list) {

    MEMBLOCK* mb = mb_list;

    while(mb) {

        fprintf(f, "Process ID: %ld\r\n", GetProcessId(mb->hProc));

        fprintf(f, "0x%08x %d\r\n", mb->addr, mb->size);

        for(unsigned int i = 0; i < mb->size; ++i) {

            fprintf(f, "%c", mb->buffer[i]);

            //if(i % 8 == 0)

            //    fprintf(f, " ");

            //if(i % 16 == 0)

            //    fprintf(f, "\r\n");

        }

        fprintf(f, "\r\n");

        mb = mb->next;

    }

}

 

void CMemoryScanTool::DumpScanInformation(char* szFileName, PMEMBLOCK mb_list) {

    FILE* f = fopen(szFileName, "w");

    if(f) DumpScanInformation(f, mb_list);

}

 

bool CMemoryScanTool::ScanString(const wchar_t* szInput, PMEMBLOCK mb, unsigned long& nAddr) {

    if(szInput == NULL || mb == NULL)

        return false;

 

    if(mb->size == 0)

        return false;

 

    SIZE_T nStringSize = wcslen(szInput);

 

    // shift one byte per loop (may shift 2 byte?)

    for(SIZE_T i = 0; i < mb->size - nStringSize; ++i) {

        if(memcmp(szInput, mb->buffer + i, nStringSize*2) == 0) {

            nAddr = i;

            return true;

        }

    }

    return false;

}

bool CMemoryScanTool::ScanString(const char* szInput, PMEMBLOCK mb, unsigned long& nAddr) {

    if(szInput == NULL || mb == NULL)

        return false;

 

    if(mb->size == 0)

        return false;

 

    SIZE_T nStringSize = strlen(szInput);

 

    // shift one byte per loop

    for(SIZE_T i = 0; i < mb->size - nStringSize*2; ++i) {

        if(memcmp(szInput, (mb->buffer + i), nStringSize*2) == 0) {

            nAddr = (unsigned long)(mb->addr) + i;

            return true;

        }

    }

    return false;

}

 

bool CMemoryScanTool::ScanAllBlock(const wchar_t* szInput, PMEMBLOCK mb_list, vector<FOUNDADDR>& vecOut) {

    MEMBLOCK* mb = mb_list;

    unsigned long nAddr;

    while(mb) {

        if(ScanString(szInput, mb, nAddr)) {

            FOUNDADDR fa;

            fa.addr = mb->addr;

            fa.offset = nAddr;

            vecOut.push_back(fa);

        }

        mb = mb->next;

    }

    return (vecOut.size() != 0);

}

 

bool CMemoryScanTool::ScanAllBlock(const char* szInput, PMEMBLOCK mb_list, vector<FOUNDADDR>& vecOut) {

    MEMBLOCK* mb = mb_list;

    unsigned long nAddr;

    while(mb) {

        if(ScanString(szInput, mb, nAddr)) {

            FOUNDADDR fa;

            fa.addr = mb->addr;

            fa.offset = nAddr;

            vecOut.push_back(fa);

        }

        mb = mb->next;

    }

    return (vecOut.size() != 0);

}

 

 

Really easy to understand. This program use only 3 windows API. 

  1. VirtualQueryEx
  2. ReadProcessMemory
  3. OpenProcess
You can see that the 3 functions is documented on MSDN. This is a teaching article on the Youtube. You can search the keyword "memory scan" to find it. I write the video to an text format. Everyone can use it free.

 

Memory Scan Tool

This will not too complex. It a easy way to let you scan your process memory like some game cheater. Of course, there is a free tool named "Game Cheater". That is open source one.

But I just want to write a "string" scan for myself. You can modify this by your self. Totally free.

See the definition.

#ifndef __MEMORY_SCAN_TOOL_H__

#define __MEMORY_SCAN_TOOL_H__

 

#pragma once

 

#include <vector>

 

typedef struct _MEMBLOCK {

    DWORD pid;                      // process id

    HANDLE hProc;                   // handle of the process

    unsigned char * addr;           // block address (start address)

    SIZE_T size;                    // memory block size

    unsigned char * buffer;         // a copy buffer of this memory block

    unsigned char * searchmask;     // used for searching

    SIZE_T matches;                 // used for searching

    SIZE_T data_size;               // data size when search

    struct _MEMBLOCK * next;        // linked list

} MEMBLOCK, *PMEMBLOCK;

 

typedef struct _FOUNDADDR {

    unsigned char * addr;           // base address

    unsigned long offset;           // off set

} FOUNDADDR;

 

//#define IS_IN_SEARCH(mb, offset) (mb->searchmask[(offset)/8] & (1 << ((offset)%8)))

//#define REMOVE_FROM_SEARCH(mb, offset) mb->searchmask[(offset)/8] &= ~(1 << ((offset)%8));

 

///

// This class is used for memory scan

// 1. Get memory block for scaning

// 2. Get the scan result

///

class CMemoryScanTool

{

public:

    CMemoryScanTool();

    ~CMemoryScanTool();

 

    static CMemoryScanTool* GetMemoryScanToolInstance();

    static void ReleaseMemoryScanToolInstance();

 

    // create the memory list by using CreateMemoryBlock

    PMEMBLOCK CreateMemoryList(unsigned int pid, int data_size);

    // create the memory block

    PMEMBLOCK CreateMemoryBlock(HANDLE hProc, MEMORY_BASIC_INFORMATION* meminfo, int data_size);

    // free the memory block (one block with buffer)

    void FreeMemoryBlock(PMEMBLOCK mb);

    // free the memory block list (by using FreeMemoryBlock)

    void FreeMemoryBlockList(PMEMBLOCK mb_list);

    // update the memory block data by using ReadProcessMemory

    void UpdateMemoryBlock(PMEMBLOCK mb);

    // Dump the information into a file

    void DumpScanInformation(FILE* f, PMEMBLOCK mb_list);

    void DumpScanInformation(char* szFileName, PMEMBLOCK mb_list);

 

    // scan the input string in the memory block list

    // Return Value: true indicate that the input string is in the memory block, the nAddr indicate the offset of this block

    bool ScanString(const wchar_t* szInput, PMEMBLOCK mb, unsigned long& nAddr);

    bool ScanString(const char* szInput, PMEMBLOCK mb, unsigned long& nAddr);

 

    // scan all memory block by using ScanString

    bool ScanAllBlock(const wchar_t* szInput, PMEMBLOCK mb_list, std::vector<FOUNDADDR>& vecOut);

    bool ScanAllBlock(const char* szInput, PMEMBLOCK mb_list, std::vector<FOUNDADDR>& vecOut);

 

};

 

#endif __MEMORY_SCAN_TOOL_H__


See the implementation.

#include "StdAfx.h"

#include "MemoryScanTool.h"

 

using namespace std;

 

namespace {

    static CMemoryScanTool* g_pMemoryScanTool = NULL;

    typedef LONG  NTSTATUS;

    typedef NTSTATUS (WINAPI *pFnRtlAdjustPrivilege)(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled);

    pFnRtlAdjustPrivilege  RtlAdjustPrivilege;

}

 

CMemoryScanTool::CMemoryScanTool()

{

    RtlAdjustPrivilege = (pFnRtlAdjustPrivilege)GetProcAddress(LoadLibrary(_T("Ntdll.dll")), "RtlAdjustPrivilege");

    BOOLEAN bEnabled = FALSE;

    // get debug privilege

    RtlAdjustPrivilege(20, 1, 0, &bEnabled);;

}

 

CMemoryScanTool::~CMemoryScanTool()

{

}

 

CMemoryScanTool* CMemoryScanTool::GetMemoryScanToolInstance() {

    if(g_pMemoryScanTool == NULL)

        g_pMemoryScanTool = new CMemoryScanTool();

    return g_pMemoryScanTool;

}

 

void CMemoryScanTool::ReleaseMemoryScanToolInstance() {

    if(g_pMemoryScanTool)

        delete g_pMemoryScanTool;

    g_pMemoryScanTool = NULL;

}

 

PMEMBLOCK CMemoryScanTool::CreateMemoryList(unsigned int pid, int data_size) {

    MEMBLOCK* mb_list = NULL;

    MEMORY_BASIC_INFORMATION meminfo;

    unsigned char* addr = 0;

    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

    if(hProc) {

        while(true) {

            if(VirtualQueryEx(hProc, addr, &meminfo, sizeof(meminfo)) == 0)

                break;

#define WRITABLE (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)

            if((meminfo.State & MEM_COMMIT) && (meminfo.Protect & WRITABLE)) {

                MEMBLOCK* mb = CreateMemoryBlock(hProc, &meminfo, data_size);

                if(mb) {

                    UpdateMemoryBlock(mb);

                    mb->pid = pid;

                    mb->next = mb_list;

                    mb_list = mb;

                }

            }

            addr = (unsigned char*)meminfo.BaseAddress + meminfo.RegionSize;

        }

    }

    return mb_list;

}

 

PMEMBLOCK CMemoryScanTool::CreateMemoryBlock(HANDLE hProc, MEMORY_BASIC_INFORMATION* meminfo, int data_size) {

    PMEMBLOCK mb = (PMEMBLOCK)malloc(sizeof(MEMBLOCK));

    if(mb) {

        mb->hProc = hProc;

        mb->addr = (unsigned char*)meminfo->BaseAddress;

        mb->size = meminfo->RegionSize;

        mb->buffer = (unsigned char*)malloc(meminfo->RegionSize);

        mb->searchmask = (unsigned char*)malloc(meminfo->RegionSize/8);

        memset(mb->searchmask, 0xff, meminfo->RegionSize/8);    // mark the byte is in searching.

        mb->matches = meminfo->RegionSize;

        mb->data_size = data_size;

        mb->next = NULL;

    }

    return mb;

}

 

void CMemoryScanTool::FreeMemoryBlock(PMEMBLOCK mb) {

    if(mb) {

        if(mb->buffer)

            free(mb->buffer);

 

        if(mb->searchmask)

            free(mb->searchmask);

 

        free(mb);

    }

}

 

void CMemoryScanTool::FreeMemoryBlockList(PMEMBLOCK mb_list) {

    CloseHandle(mb_list->hProc);

    while(mb_list) {

        PMEMBLOCK mb = mb_list;

        mb_list = mb_list->next;

        FreeMemoryBlock(mb);

    }

}

 

// Read the memory data from a process id in MEMBLOCK

void CMemoryScanTool::UpdateMemoryBlock(PMEMBLOCK mb)

{

    static unsigned char tmpbuf[128*1024];

    SIZE_T bytes_left = 0;

    SIZE_T total_read = 0;

    SIZE_T bytes_to_read = 0;

    SIZE_T bytes_read = 0;

 

    if(mb->matches > 0) {

        bytes_left = mb->size;

        mb->matches = 0;

        while(bytes_left) {

            bytes_to_read = (bytes_left > sizeof(tmpbuf)) ? sizeof(tmpbuf) : bytes_left;

            ReadProcessMemory(mb->hProc, mb->addr + total_read, tmpbuf, bytes_to_read, (SIZE_T *)&bytes_read);

            if(bytes_read != bytes_to_read) break;

            memcpy(mb->buffer + total_read, tmpbuf, bytes_read);

            bytes_left -= bytes_read;

            total_read += bytes_read;

        }

        mb->size = total_read;

    }

}

 

 

void CMemoryScanTool::DumpScanInformation(FILE* f, PMEMBLOCK mb_list) {

    MEMBLOCK* mb = mb_list;

    while(mb) {

        fprintf(f, "Process ID: %ldrn", GetProcessId(mb->hProc));

        fprintf(f, "0x%08x %drn", mb->addr, mb->size);

        for(unsigned int i = 0; i < mb->size; ++i) {

            fprintf(f, "%c", mb->buffer[i]);

            //if(i % 8 == 0)

            //    fprintf(f, " ");

            //if(i % 16 == 0)

            //    fprintf(f, "rn");

        }

        fprintf(f, "rn");

        mb = mb->next;

    }

}

 

void CMemoryScanTool::DumpScanInformation(char* szFileName, PMEMBLOCK mb_list) {

    FILE* f = fopen(szFileName, "w");

    if(f) DumpScanInformation(f, mb_list);

}

 

bool CMemoryScanTool::ScanString(const wchar_t* szInput, PMEMBLOCK mb, unsigned long& nAddr) {

    if(szInput == NULL || mb == NULL)

        return false;

 

    if(mb->size == 0)

        return false;

 

    SIZE_T nStringSize = wcslen(szInput);

 

    // shift one byte per loop (may shift 2 byte?)

    for(SIZE_T i = 0; i < mb->size - nStringSize; ++i) {

        if(memcmp(szInput, mb->buffer + i, nStringSize*2) == 0) {

            nAddr = i;

            return true;

        }

    }

    return false;

}

bool CMemoryScanTool::ScanString(const char* szInput, PMEMBLOCK mb, unsigned long& nAddr) {

    if(szInput == NULL || mb == NULL)

        return false;

 

    if(mb->size == 0)

        return false;

 

    SIZE_T nStringSize = strlen(szInput);

 

    // shift one byte per loop

    for(SIZE_T i = 0; i < mb->size - nStringSize*2; ++i) {

        if(memcmp(szInput, (mb->buffer + i), nStringSize*2) == 0) {

            nAddr = (unsigned long)(mb->addr) + i;

            return true;

        }

    }

    return false;

}

 

bool CMemoryScanTool::ScanAllBlock(const wchar_t* szInput, PMEMBLOCK mb_list, vector<FOUNDADDR>& vecOut) {

    MEMBLOCK* mb = mb_list;

    unsigned long nAddr;

    while(mb) {

        if(ScanString(szInput, mb, nAddr)) {

            FOUNDADDR fa;

            fa.addr = mb->addr;

            fa.offset = nAddr;

            vecOut.push_back(fa);

        }

        mb = mb->next;

    }

    return (vecOut.size() != 0);

}

 

bool CMemoryScanTool::ScanAllBlock(const char* szInput, PMEMBLOCK mb_list, vector<FOUNDADDR>& vecOut) {

    MEMBLOCK* mb = mb_list;

    unsigned long nAddr;

    while(mb) {

        if(ScanString(szInput, mb, nAddr)) {

            FOUNDADDR fa;

            fa.addr = mb->addr;

            fa.offset = nAddr;

            vecOut.push_back(fa);

        }

        mb = mb->next;

    }

    return (vecOut.size() != 0);

}

 

Really easy to unders

2010年5月31日 星期一

How to get the named Mutex and Event in your system.

Once you want to see the objects in you computer. The most important is the Mutants and the Events. But how to enumerate all the object in you computer. Sorry about that there is no easy way to get these information.

After research a while, I found the un-documented API from NTDLL.dll. There are some functions can do this work. So, I export by using LoadLibrary and GetProcAddress.

Please reference the code as following.

 


The following is the prototype definition:

 

#pragma once
#include <string>
#include <map>
#include <vector>

using namespace std;

#ifndef NtCurrentProcess
	#define NtCurrentProcess() ((HANDLE)0xFFFFFFFF)
#endif /* NtCurrentProcess */
#ifndef NtCurrentThread
	#define NtCurrentThread() ((HANDLE)0xFFFFFFFE)
#endif /* NtCurrentThread */

#ifndef NT_SUCCESS
	#define NT_SUCCESS(x) ((x)>=0)
	#define STATUS_SUCCESS ((NTSTATUS)0)
#endif 

#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004)
#define DUPLICATE_SAME_ATTRIBUTES 0x00000004

typedef DWORD ULONG;
typedef WORD USHORT;
typedef ULONG NTSTATUS;

///////////////////////////////////////////////////
typedef enum _SYSTEM_INFORMATION_CLASS {
	SystemHandleInformation = 16, 
} SYSTEM_INFORMATION_CLASS, * PSYSTEM_INFORMATION_CLASS;

typedef struct _UNICODE_STRING {
	USHORT Length;
	USHORT MaximumLength;
	PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
	USHORT UniqueProcessId;
	USHORT CreatorBackTraceIndex;
	UCHAR ObjectTypeIndex;
	UCHAR HandleAttributes;
	USHORT HandleValue;
	PVOID Object;
	ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION {
	ULONG ProcessId;
	UCHAR ObjectTypeNumber;
	UCHAR Flags;
	USHORT Handle;
	PVOID Object;
	ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

typedef struct _OBJECT_BASIC_INFORMATION {
	ULONG Attributes;
	ACCESS_MASK GrantedAccess;
	ULONG HandleCount;
	ULONG PointerCount;
	ULONG PagedPoolUsage;
	ULONG NonPagedPoolUsage;
	ULONG Reserved[3];
	ULONG NameInformationLength;
	ULONG TypeInformationLength;
	ULONG SecurityDescriptorLength;
	LARGE_INTEGER CreateTime;
} OBJECT_BASIC_INFORMATION, * POBJECT_BASIC_INFORMATION;

typedef struct _OBJECT_TYPE_INFORMATION {
	UNICODE_STRING TypeName;
	ULONG TotalNumberOfObjects;
	ULONG TotalNumberOfHandles;
	ULONG TotalPagedPoolUsage;
	ULONG TotalNonPagedPoolUsage;
	ULONG TotalNamePoolUsage;
	ULONG TotalHandleTableUsage;
	ULONG HighWaterNumberOfObjects;
	ULONG HighWaterNumberOfHandles;
	ULONG HighWaterPagedPoolUsage;
	ULONG HighWaterNonPagedPoolUsage;
	ULONG HighWaterNamePoolUsage;
	ULONG HighWaterHandleTableUsage;
	ULONG InvalidAttributes;
	GENERIC_MAPPING GenericMapping;
	ULONG ValidAccessMask;
	BOOLEAN SecurityRequired;
	BOOLEAN MaintainHandleCount;
	ULONG PoolType;
	ULONG DefaultPagedPoolCharge;
	ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

typedef enum _OBJECT_INFORMATION_CLASS {
	ObjectBasicInformation, ObjectNameInformation, ObjectTypeInformation, ObjectAllInformation, ObjectDataInformation
} OBJECT_INFORMATION_CLASS, * POBJECT_INFORMATION_CLASS;

typedef struct _OBJECT_NAME_INFORMATION {
	UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION;

typedef NTSTATUS (NTAPI* PZwQuerySystemInformation)(IN ULONG SystemInformationClass, OUT PVOID SystemInformation, IN ULONG Length, OUT PULONG ResultLength);

typedef NTSTATUS (NTAPI* PZwDuplicateObject)(IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, IN HANDLE TargetProcessHandle, OUT PHANDLE TargetHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Options);

typedef NTSTATUS (NTAPI* PZwQueryObject)(IN HANDLE ObjectHandle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG Length, OUT PULONG ResultLength OPTIONAL);

typedef NTSTATUS (WINAPI *pFnRtlAdjustPrivilege)(ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled);

typedef struct _ProcessInformation {
    DWORD dwPID;                                // Process ID
    wstring strProcessName;                     // Process Name
    wstring strProcessImagePath;                // Process Image file path
    HANDLE handle;
    wstring EventName;    // Event Name
    wstring MutexName;    // Mutex Name
} ProcessInformation;

class CInformer
{
public:
    CInformer(void);
    ~CInformer(void);
    void GetProcessInformation(vector<ProcessInformation>& vecPI);
    wstring GetProcessNameByID(DWORD pid);
private:
    BOOL setPrivilege(HANDLE hToken, LPCTSTR szPrivilege, BOOL bOk);
    vector<ProcessInformation> m_vecInformer;

};

The following is the implementing file content.
#include "StdAfx.h"
#include "Informer.h"
#include <Tlhelp32.h>

namespace {
    PZwQuerySystemInformation ZwQuerySystemInformation = NULL;
    PZwDuplicateObject ZwDuplicateObject = NULL;
    PZwQueryObject ZwQueryObject = NULL;
    pFnRtlAdjustPrivilege RtlAdjustPrivilege = NULL;
}

CInformer::CInformer(void)
{
    if(!(ZwQuerySystemInformation = (PZwQuerySystemInformation)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "ZwQuerySystemInformation"))) {
        ASSERT(FALSE);
    }
    if(!(ZwDuplicateObject = (PZwDuplicateObject)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "ZwDuplicateObject"))) {
        ASSERT(FALSE);
    }
    if(!(ZwQueryObject = (PZwQueryObject)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "ZwQueryObject"))) {
        ASSERT(FALSE);
    }

    BOOL bRet = FALSE;
    HANDLE hToken;
    bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
    if(!bRet) {
        ASSERT(FALSE);
    }

    bRet = setPrivilege(hToken, SE_DEBUG_NAME, TRUE);

    if(!bRet) {
        ASSERT(FALSE);
    }
}

CInformer::~CInformer(void)
{
}

BOOL CInformer::setPrivilege(HANDLE hToken, LPCTSTR szPrivilege, BOOL bOk) {
    BOOL bRet = FALSE;
    LUID luid;
    TOKEN_PRIVILEGES tp;

    bRet = LookupPrivilegeValue(NULL, szPrivilege, &luid);
    if(!bRet) {
        return bRet;
    }

    tp.PrivilegeCount = 1;

    if(bOk) {
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    } else {
        tp.Privileges[0].Attributes = NULL;
    }

    bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL);

    if(!bRet) {
        return bRet;
    }
    return TRUE;
}

wstring CInformer::GetProcessNameByID(DWORD pid) {
    HANDLE hSnapshot;
    PROCESSENTRY32 pe;
    BOOL fFound = FALSE;

    pe.dwSize = sizeof(PROCESSENTRY32);
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hSnapshot != NULL) {
        fFound = Process32First(hSnapshot, &pe);
        while(fFound) {
            if(pid == pe.th32ProcessID) {
                CloseHandle(hSnapshot);
                return pe.szExeFile;
            }
            fFound = Process32Next(hSnapshot, &pe);
        }
        CloseHandle(hSnapshot);
    }
    return L"";
}


void CInformer::GetProcessInformation(vector<ProcessInformation>& vecPI) {
    ULONG n = 0x4000;
    PULONG p = new ULONG[n];

    // Get all system information from system handles
    while(ZwQuerySystemInformation(SystemHandleInformation, p, n* sizeof * p, 0) == STATUS_INFO_LENGTH_MISMATCH) {
        delete [] p;
        p = new ULONG[n *= 2];
    }

    // indicate the array to a handle information structure
    PSYSTEM_HANDLE_INFORMATION h = PSYSTEM_HANDLE_INFORMATION(p + 1);

    // Query handle information

    for(ULONG i = 0; i < * p; i++) {
        HANDLE hProcess;
        // if the process is system process, ingore it.
        if(h[i].ProcessId == 0 || h[i].ProcessId == 4 || (hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, h[i].ProcessId)) == NULL) continue;

        DWORD dwQueryPID = h[i].ProcessId;

        HANDLE hObject;
        if(ZwDuplicateObject(hProcess, HANDLE(h[i].Handle), NtCurrentProcess(), &hObject, 0, 0, DUPLICATE_SAME_ATTRIBUTES) != STATUS_SUCCESS) {
            continue;
        }
        OBJECT_BASIC_INFORMATION obi;
        ZwQueryObject(hObject, ObjectBasicInformation, &obi, sizeof obi, &n);

        n = obi.TypeInformationLength + 2; // extend the length to get the type name

        POBJECT_TYPE_INFORMATION oti = (POBJECT_TYPE_INFORMATION) (new char[n]);

        ZwQueryObject(hObject, ObjectTypeInformation, oti, n, &n);
        n = obi.NameInformationLength == 0 ? MAX_PATH* sizeof(WCHAR): obi.NameInformationLength;

        POBJECT_NAME_INFORMATION oni = (POBJECT_NAME_INFORMATION) (new char[n]);

        NTSTATUS rv = ZwQueryObject(hObject, ObjectNameInformation, oni, n, &n);

        if(oni[0].Name.Buffer == NULL || wcscmp(oni[0].Name.Buffer, L"") == 0) {
            delete [] (char*)oti;
            delete [] (char*)oni;
            continue;
        }

        ProcessInformation pi;

        if(wcscmp(oti[0].TypeName.Buffer, L"Event") != 0 && wcscmp(oti[0].TypeName.Buffer, L"Mutant") != 0) {
            goto clean;
        }

        if(wcscmp(oti[0].TypeName.Buffer, L"") == 0)
            goto clean;

        if(wcscmp(oti[0].TypeName.Buffer, L"Event") == 0) {
            pi.EventName = oni[0].Name.Buffer;
        } else if(wcscmp(oti[0].TypeName.Buffer, L"Mutant") == 0) {
            pi.MutexName = oni[0].Name.Buffer;
        }
        pi.handle = HANDLE(h[i].Handle);
        pi.dwPID = h[i].ProcessId;
        pi.strProcessName = GetProcessNameByID(h[i].ProcessId);
        vecPI.push_back(pi);

clean:
        delete [] (char*)oti;
        delete [] (char*)oni;

    }
    delete [] p;
}

After calling Informer::GetProcessInformation, you can get the Mutex and Events in your computer.

2010年2月20日 星期六

Wii Games Manager - MiiGame

This is a Wii games manager such WBFS manager. It's named MiiGame. Please reference my project site from here. WBFS is a file system for wii games. Please use these tools for your USB mass storages for Wii.
This project is written by internationalization purposes. So, you can modify the language into your own localization.
This project is just starting. Please give me more information to refine my project. If you want to join my project, please leave your messages for me.
The following is the screenshot for MiiGame.