关于作者

用户名:EmZoo
笔名:EmZoo
地区: 上海
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



博客摘引

包罗万象的嵌入式资源

访问统计:
文章个数:105
评论个数:25
留言条数:5




Powered by BlogDriver 2.1

万象嵌入式资源站

 

深入腹地, 探个究竟

文章

MMS协议解析2 (原创)  (作者置顶)
摘要:MMS协议解析第二部分 查看全文

- 作者: EmZoo 2006年07月17日, 星期一 15:00  回复(2) |  引用(0) 加入博采

MMS协议解析1(原创)  (作者置顶)
摘要:MMS协议的分析 查看全文

- 作者: EmZoo 2006年07月17日, 星期一 12:58  回复(0) |  引用(0) 加入博采

PPC_Linux启动流程及移植(原创)——2、内核引导前传  (作者置顶)
摘要:PPC linux启动流程2.从vmlinux到start_kernel. 查看全文

- 作者: EmZoo 2006年03月21日, 星期二 16:13  回复(1) |  引用(0) 加入博采

PPC_Linux启动流程及移植(原创)1--zImage的解压缩  (作者置顶)
摘要:PPC_Linux移植和启动流程。 查看全文

- 作者: EmZoo 2006年03月14日, 星期二 13:18  回复(1) |  引用(0) 加入博采

对于VIVIBootLoader的分析(原创)  (作者置顶)
摘要:ViVi Bootloader的分析,大学毕业时候写的。拼凑了一个优秀毕业论文:) 查看全文

- 作者: EmZoo 2005年06月9日, 星期四 17:40  回复(0) |  引用(0) 加入博采

Build Android for Moto Milestone
摘要:Build and develop for milestone android . 查看全文

- 作者: EmZoo 2011年02月1日, 星期二 09:17  回复(0) |  引用(0) 加入博采

代码注入的三种方法

代码注入的三种方法

作者:Robert Kuster
编译:VCKBASE

 

原文出处:Three Ways to Inject Your Code into Another Process

下载源代码
 

目录

简介

  本文将讨论如何把代码注入不同的进程地址空间,然后在该进程的上下文中执行注入的代码。 我们在网上可以查到一些窗口/密码侦测的应用例子,网上的这些程序大多都依赖 Windows 钩子技术来实现。本文将讨论除了使用 Windows 钩子技术以外的其它技术来实现这个功能。如图一所示:

图一 WinSpy 密码侦测程序

为了找到解决问题的方法。首先让我们简单回顾一下问题背景。
  要“读取”某个控件的内容——无论这个控件是否属于当前的应用程序——通常都是发送 WM_GETTEXT 消息来实现。这个技术也同样应用到编辑控件,但是如果该编辑控件属于另外一个进程并设置了 ES_PASSWORD 式样,那么上面讲的方法就行不通了。用 WM_GETTEXT 来获取控件的内容只适用于进程“拥有”密码控件的情况。所以我们的问题变成了如何在另外一个进程的地址空间执行:

::SendMessage( hPwdEdit, WM_GETTEXT, nMaxChars, psBuffer );

通常有三种可能性来解决这个问题。

  1. 将你的代码放入某个 DLL,然后通过 Windows 钩子映射该DLL到远程进程;
  2. 将你的代码放入某个 DLL,然后通过 CreateRemoteThread 和 LoadLibrary 技术映射该DLL到远程进程;
  3. 如果不写单独的 DLL,可以直接将你的代码拷贝到远程进程——通过 WriteProcessMemory——并用 CreateRemoteThread 启动它的执行。本文将在第三部分详细描述该技术实现细节;

第一部分: Windows 钩子

范例程序——参见HookSpy 和HookInjEx

  Windows 钩子主要作用是监控某些线程的消息流。通常我们将钩子分为本地钩子和远程钩子以及系统级钩子,本地钩子一般监控属于本进程的线程的消息流,远程钩子是线程专用的,用于监控属于另外进程的线程消息流。系统级钩子监控运行在当前系统中的所有线程的消息流。
  如果钩子作用的线程属于另外的进程,那么你的钩子过程必须驻留在某个动态链接库(DLL)中。然后系统映射包含钩子过程的DLL到钩子作用的线程的地址空间。Windows将映射整个 DLL,而不仅仅是钩子过程。这就是为什么 Windows 钩子能被用于将代码注入到别的进程地址空间的原因。
  本文我不打算涉及钩子的具体细节(关于钩子的细节请参见 MSDN 库中的 SetWindowHookEx API),但我在此要给出两个很有用心得,在相关文档中你是找不到这些内容的:

  1. 在成功调用 SetWindowsHookEx 后,系统自动映射 DLL 到钩子作用的线程地址空间,但不必立即发生映射,因为 Windows 钩子都是消息,DLL 在消息事件发生前并没有产生实际的映射。例如:
      如果你安装一个钩子监控某些线程(WH_CALLWNDPROC)的非队列消息,在消息被实际发送到(某些窗口的)钩子作用的线程之前,该DLL 是不会被映射到远程进程的。换句话说,如果 UnhookWindowsHookEx 在某个消息被发送到钩子作用的线程之前被调用,DLL 根本不会被映射到远程进程(即使 SetWindowsHookEx 本身调用成功)。为了强制进行映射,在调用 SetWindowsHookEx 之后马上发送一个事件到相关的线程。
      在UnhookWindowsHookEx了之后,对于没有映射的DLL处理方法也一样。只有在足够的事件发生后,DLL才会有真正的映射。
  2. 当你安装钩子后,它们可能影响整个系统得性能(尤其是系统级钩子),但是你可以很容易解决这个问题,如果你使用线程专用钩子的DLL映射机制,并不截获消息。考虑使用如下代码:
    BOOL APIENTRY DllMain( HANDLE hModule,
    DWORD ul_reason_for_call,
    LPVOID lpReserved )
    {
    if( ul_reason_for_call == DLL_PROCESS_ATTACH )
    {
    // Increase reference count via LoadLibrary
    char lib_name[MAX_PATH];
    ::GetModuleFileName( hModule, lib_name, MAX_PATH );
    ::LoadLibrary( lib_name );

    // Safely remove hook
    ::UnhookWindowsHookEx( g_hHook );
    }
    return TRUE;
    }
      那么会发生什么呢?首先我们通过Windows 钩子将DLL映射到远程进程。然后,在DLL被实际映射之后,我们解开钩子。通常当第一个消息到达钩子作用线程时,DLL此时也不会被映射。这里的处理技巧是调用LoadLibrary通过增加 DLLs的引用计数来防止映射不成功。
      现在剩下的问题是如何卸载DLL,UnhookWindowsHookEx 是不会做这个事情的,因为钩子已经不作用于线程了。你可以像下面这样做:

    • 就在你想要解除DLL映射前,安装另一个钩子;
    • 发送一个“特殊”消息到远程线程;
    • 在钩子过程中截获这个消息,响应该消息时调用 FreeLibrary 和 UnhookWindowsHookEx;

      目前只使用了钩子来从处理远程进程中DLL的映射和解除映射。在此“作用于线程的”钩子对性能没有影响。
    下面我们将讨论另外一种方法,这个方法与 LoadLibrary 技术的不同之处是DLL的映射机制不会干预目标进程。相对LoadLibrary 技术,这部分描述的方法适用于 WinNT和Win9x。
      但是,什么时候使用这个技巧呢?答案是当DLL必须在远程进程中驻留较长时间(即如果你子类化某个属于另外一个进程的控件时)以及你想尽可能少的干涉目标进程时。我在 HookSpy 中没有使用它,因为注入DLL 的时间并不长——注入时间只要足够得到密码即可。我提供了另外一个例子程序——HookInjEx——来示范。HookInjEx 将DLL映射到资源管理器“explorer.exe”,并从中/解除影射,它子类化“开始”按钮,并交换鼠标左右键单击“开始”按钮的功能。

HookSpy 和 HookInjEx 的源代码都可以从本文的下载源代码中获得。

第二部分:CreateRemoteThread 和 LoadLibrary 技术

范例程序——LibSpy

通常,任何进程都可以通过 LoadLibrary API 动态加载DLL。但是,如何强制一个外部进程调用这个函数呢?答案是:CreateRemoteThread。
首先,让我们看一下 LoadLibrary 和FreeLibrary API 的声明:

HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName // 库模块文件名的地址
);

BOOL FreeLibrary(
HMODULE hLibModule // 要加载的库模块的句柄
);

现在将它们与传递到 CreateRemoteThread 的线程例程——ThreadProc 的声明进行比较。

DWORD WINAPI ThreadProc(
LPVOID lpParameter // 线程数据
);

你可以看到,所有函数都使用相同的调用规范并都接受 32位参数,返回值的大小都相同。也就是说,我们可以传递一个指针到LoadLibrary/FreeLibrary 作为到 CreateRemoteThread 的线程例程。但这里有两个问题,请看下面对CreateRemoteThread 的描述:

  1. CreateRemoteThread 的 lpStartAddress 参数必须表示远程进程中线程例程的开始地址。
  2. 如果传递到 ThreadFunc 的参数lpParameter——被解释为常规的 32位值(FreeLibrary将它解释为一个 HMODULE),一切OK。但是,如果 lpParameter 被解释为一个指针(LoadLibraryA将它解释为一个串指针)。它必须指向远程进程的某些数据。

  第一个问题实际上是由它自己解决的。LoadLibrary 和 FreeLibray 两个函数都在 kernel32.dll 中。因为必须保证kernel32存在并且在每个“常规”进程中的加载地址要相同,LoadLibrary/FreeLibray 的地址在每个进程中的地址要相同,这就保证了有效的指针被传递到远程进程。
  第二个问题也很容易解决。只要通过 WriteProcessMemory 将 DLL 模块名(LoadLibrary需要的DLL模块名)拷贝到远程进程即可。

所以,为了使用CreateRemoteThread 和 LoadLibrary 技术,需要按照下列步骤来做:

  1. 获取远程进程(OpenProcess)的 HANDLE;
  2. 为远程进程中的 DLL名分配内存(VirtualAllocEx);
  3. 将 DLL 名,包含全路径名,写入分配的内存(WriteProcessMemory);
  4. 用 CreateRemoteThread 和 LoadLibrary. 将你的DLL映射到远程进程;
  5. 等待直到线程终止(WaitForSingleObject),也就是说直到 LoadLibrary 调用返回。另一种方法是,一旦 DllMain(用DLL_PROCESS_ATTACH调用)返回,线程就会终止;
  6. 获取远程线程的退出代码(GetExitCodeThread)。注意这是一个 LoadLibrary 返回的值,因此是所映射 DLL 的基地址(HMODULE)。
    在第二步中释放分配的地址(VirtualFreeEx);
  7. 用 CreateRemoteThread 和 FreeLibrary从远程进程中卸载 DLL。传递在第六步获取的 HMODULE 句柄到 FreeLibrary(通过 CreateRemoteThread 的lpParameter参数);
  8. 注意:如果你注入的 DLL 产生任何新的线程,一定要在卸载DLL 之前将它们都终止掉;
  9. 等待直到线程终止(WaitForSingleObject);

  此外,处理完成后不要忘了关闭所有句柄,包括在第四步和第八步创建的两个线程以及在第一步获取的远程线程句柄。现在让我们看一下 LibSpy 的部分代码,为了简单起见,上述步骤的实现细节中的错误处理以及 UNICODE 支持部分被略掉。

HANDLE hThread;
char szLibPath[_MAX_PATH]; // “LibSpy.dll”模块的名称 (包括全路径);
void* pLibRemote; // 远程进程中的地址,szLibPath 将被拷贝到此处;
DWORD hLibModule; // 要加载的模块的基地址(HMODULE)
HMODULE hKernel32 = ::GetModuleHandle("Kernel32");

// 初始化szLibPath
//...
// 1. 在远程进程中为szLibPath 分配内存
// 2. 将szLibPath 写入分配的内存
pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath),
MEM_COMMIT, PAGE_READWRITE );
::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath,
sizeof(szLibPath), NULL );

// 将"LibSpy.dll" 加载到远程进程(使用CreateRemoteThread 和 LoadLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,
"LoadLibraryA" ),
pLibRemote, 0, NULL );
::WaitForSingleObject( hThread, INFINITE );

// 获取所加载的模块的句柄
::GetExitCodeThread( hThread, &hLibModule );

// 清除
::CloseHandle( hThread );
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
  假设我们实际想要注入的代码——SendMessage ——被放在DllMain (DLL_PROCESS_ATTACH)中,现在它已经被执行。那么现在应该从目标进程中将DLL 卸载:
// 从目标进程中卸载"LibSpy.dll"  (使用 CreateRemoteThread 和 FreeLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,
"FreeLibrary" ),
(void*)hLibModule, 0, NULL );
::WaitForSingleObject( hThread, INFINITE );

// 清除
::CloseHandle( hThread );
进程间通信

  到目前为止,我们只讨论了关于如何将DLL 注入到远程进程的内容,但是,在大多数情况下,注入的 DLL 都需要与原应用程序进行某种方式的通信(回想一下,我们的DLL是被映射到某个远程进程的地址空间里了,不是在本地应用程序的地址空间中)。比如秘密侦测 程序,DLL必须要知道实际包含密码的控件句柄,显然,编译时无法将这个值进行硬编码。同样,一旦DLL获得了秘密,它必须将它发送回原应用程序,以便能 正确显示出来。
  幸运的是,有许多方法处理这个问题,文件映射,WM_COPYDATA,剪贴板以及很简单的 #pragma data_seg 共享数据段等,本文我不打算使用这些技术,因为MSDN(“进程间通信”部分)以及其它渠道可以找到很多文档参考。不过我在 LibSpy例子中还是使用了 #pragma data_seg。细节请参考 LibSpy 源代码。

第三部分:CreateRemoteThread 和 WriteProcessMemory 技术

范例程序——WinSpy

  另外一个将代码拷贝到另一个进程地址空间并在该进程上下文中执行的方法是使用远程线程和 WriteProcessMemory API。这种方法不用编写单独的DLL,而是用 WriteProcessMemory 直接将代码拷贝到远程进程——然后用 CreateRemoteThread 启动它执行。先来看看 CreateRemoteThread 的声明:

HANDLE CreateRemoteThread(
HANDLE hProcess, // 传入创建新线程的进程句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性指针
DWORD dwStackSize, // 字节为单位的初始线程堆栈
LPTHREAD_START_ROUTINE lpStartAddress, // 指向线程函数的指针
LPVOID lpParameter, // 新线程使用的参数
DWORD dwCreationFlags, // 创建标志
LPDWORD lpThreadId // 指向返回的线程ID
);
如果你比较它与 CreateThread(MSDN)的声明,你会注意到如下的差别:
  • 在 CreateRemoteThread中,hProcess是额外的一个参数,一个进程句柄,新线程就是在这个进程中创建的;
  • 在 CreateRemoteThread中,lpStartAddress 表示的是在远程进程地址空间中的线程起始地址。线程函数必须要存在于远程进程中,所以我们不能简单地传递一个指针到本地的 ThreadFunc。必须得先拷贝代码到远程进程;
  • 同样,lpParameter 指向的数据也必须要存在于远程进程,所以也得将它拷贝到那。

综上所述,我们得按照如下的步骤来做:

  1. 获取一个远程进程的HANDLE (OpenProces) ;
  2. 在远程进程地址空间中为注入的数据分配内存(VirtualAllocEx);
  3. 将初始的 INDATA 数据结构的一个拷贝写入分配的内存中(WriteProcessMemory);
  4. 在远程进程地址空间中为注入的代码分配内存;
  5. 将 ThreadFunc 的一个拷贝写入分配的内存;
  6. 用 CreateRemoteThread启动远程的 ThreadFunc 拷贝;
  7. 等待远程线程终止(WaitForSingleObject);
  8. 获取远程来自远程进程的结果(ReadProcessMemory 或 GetExitCodeThread);
  9. 释放在第二步和第四步中分配的内存(VirtualFreeEx);
  10. 关闭在第六步和第一步获取的句柄(CloseHandle);

ThreadFunc 必须要遵循的原则:

  1. 除了kernel32.dll 和user32.dll 中的函数之外,ThreadFunc 不要调用任何其它函数,只有 kernel32.dll 和user32.dll被保证在本地和目标进程中的加载地址相同(注意,user32.dll并不是被映射到每个 Win32 的进程)。如果你需要来自其它库中的函数,将LoadLibrary 和 GetProcAddress 的地址传给注入的代码,然后放手让它自己去做。如果映射到目标进程中的DLL有冲突,你也可以用 GetModuleHandle 来代替 LoadLibrary。
      同样,如果你想在 ThreadFunc 中调用自己的子例程,要单独把每个例程的代码拷贝到远程进程并用 INJDATA为 ThreadFunc 提供代码的地址。
  2. 不要使用静态字符串,而要用 INJDATA 来传递所有字符串。之所以要这样,是因为编译器将静态字符串放在可执行程序的“数据段”中,可是引用(指针)是保留在代码中的。那么,远程进程中ThreadFunc 的拷贝指向的内容在远程进程的地址空间中是不存在的。
  3. 去掉 /GZ 编译器开关,它在调试版本中是默认设置的。
  4. 将 ThreadFunc 和 AfterThreadFunc 声明为静态类型,或者不启用增量链接。
  5. ThreadFunc 中的局部变量一定不能超过一页(也就是 4KB)。
    注意在调试版本中4KB的空间有大约10个字节是用于内部变量的。
  6. 如果你有一个开关语句块大于3个case 语句,将它们像下面这样拆分开:
    switch( expression ) {
    case constant1: statement1; goto END;
    case constant2: statement2; goto END;
    case constant3: statement2; goto END;
    }
    switch( expression ) {
    case constant4: statement4; goto END;
    case constant5: statement5; goto END;
    case constant6: statement6; goto END;
    }
    END:
    或者将它们修改成一个 if-else if 结构语句(参见附录E)。

  如果你没有按照这些规则来做,目标进程很可能会崩溃。所以务必牢记。在目标进程中不要假设任何事情都会像在本地进程中那样 (参见附录F)。

GetWindowTextRemote(A/W)

要想从“远程”编辑框获得密码,你需要做的就是将所有功能都封装在GetWindowTextRemot(A/W):中。

int GetWindowTextRemoteA( HANDLE hProcess, HWND hWnd, LPSTR lpString );
int GetWindowTextRemoteW( HANDLE hProcess, HWND hWnd, LPWSTR lpString );

参数说明:
hProcess:编辑框控件所属的进程句柄;
hWnd:包含密码的编辑框控件句柄;
lpString:接收文本的缓冲指针;
返回值:返回值是拷贝的字符数;

  下面让我们看看它的部分代码——尤其是注入数据的代码——以便明白 GetWindowTextRemote 的工作原理。此处为简单起见,略掉了 UNICODE 支持部分。

INJDATA
typedef LRESULT (WINAPI *SENDMESSAGE)(HWND,UINT,WPARAM,LPARAM);

typedef struct {
HWND hwnd; // 编辑框句柄
SENDMESSAGE fnSendMessage; // 指向user32.dll 中 SendMessageA 的指针

char psText[128]; // 接收密码的缓冲
} INJDATA;

  INJDATA 是一个被注入到远程进程的数据结构。但在注入之前,结构中指向 SendMessageA 的指针是在本地应用程序中初始化的。因为对于每个使用user32.dll的进程来说,user32.dll总是被映射到相同的地址,因此,SendMessageA 的地址也肯定是相同的。这就保证了被传递到远程进程的是一个有效的指针。

ThreadFunc函数

static DWORD WINAPI ThreadFunc (INJDATA *pData) 
{
pData->fnSendMessage( pData->hwnd, WM_GETTEXT, // Get password
sizeof(pData->psText),
(LPARAM)pData->psText );
return 0;
}

// 该函数在ThreadFunc之后标记内存地址
// int cbCodeSize = (PBYTE) AfterThreadFunc - (PBYTE) ThreadFunc.
static void AfterThreadFunc (void)
{
}

ThradFunc 是被远程线程执行的代码。

  • 注释:注意AfterThreadFunc 是如何计算 ThreadFunc 大小的。通常这样做并不是一个好办法,因为链接器可以随意更改函数的顺序(也就是说ThreadFunc可能被放在 AfterThreadFunc之后)。这一点你可以在小项目中很好地保证函数的顺序是预先设想好的,比如 WinSpy 程序。在必要的情况下,你还可以使用 /ORDER 链接器选项来解决函数链接顺序问题。或者用反汇编确定 ThreadFunc 函数的大小。
如何使用该技术子类化远程控件

范例程序——InjectEx

下面我们将讨论一些更复杂的内容,如何子类化属于另一个进程的控件。

首先,你得拷贝两个函数到远程进程来完成此任务

  1. ThreadFunc实际上是通过 SetWindowLong子类化远程进程中的控件;
  2. NewProc是子类化控件的新窗口过程;

  这里主要的问题是如何将数据传到远程窗口过程 NewProc,因为 NewProc 是一个回调函数,它必须遵循特定的规范和原则,我们不能简单地在参数中传递 INJDATA指针。幸运的是我找到了有两个方法来解决这个问题,只不过要借助汇编语言,所以不要忽略了汇编,关键时候它是很有用的!

方法一:

如下图所示:

  在远程进程中,INJDATA 被放在NewProc 之前,这样 NewProc 在编译时便知道 INJDATA 在远程进程地址空间中的内存位置。更确切地说,它知道相对于其自身位置的 INJDATA 的地址,我们需要所有这些信息。下面是 NewProc 的代码:

static LRESULT CALLBACK NewProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 消息标示符
WPARAM wParam, // 第一个消息参数
LPARAM lParam ) // 第二个消息参数
{
INJDATA* pData = (INJDATA*) NewProc; // pData 指向 NewProc
pData--; // 现在pData 指向INJDATA;
// 回想一下INJDATA 被置于远程进程NewProc之前;

//-----------------------------
// 此处是子类化代码
// ........
//-----------------------------

// 调用原窗口过程;
// fnOldProc (由SetWindowLong 返回) 被(远程)ThreadFunc初始化
// 并被保存在(远程)INJDATA;中
return pData->fnCallWindowProc( pData->fnOldProc,
hwnd,uMsg,wParam,lParam );
}
但这里还有一个问题,见第一行代码:
INJDATA* pData = (INJDATA*) NewProc;

  这种方式 pData得到的是硬编码值(在我们的进程中是原 NewProc 的内存地址)。这不是我们十分想要的。在远程进程中,NewProc “当前”拷贝的内存地址与它被移到的实际位置是无关的,换句话说,我们会需要某种类型的“this 指针”。
虽然用 C/C++ 无法解决这个问题,但借助内联汇编可以解决,下面是对 NewProc的修改:

static LRESULT CALLBACK NewProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 消息标示符
WPARAM wParam, // 第一个消息参数
LPARAM lParam ) // 第二个消息参数
{
// 计算INJDATA 结构的位置
// 在远程进程中记住这个INJDATA
// 被放在NewProc之前
INJDATA* pData;
_asm {
call dummy
dummy:
pop ecx // <- ECX 包含当前的EIP
sub ecx, 9 // <- ECX 包含NewProc的地址
mov pData, ecx
}
pData--;


//-----------------------------
// 此处是子类化代码
// ........
//-----------------------------

// 调用原来的窗口过程
return pData->fnCallWindowProc( pData->fnOldProc,
hwnd,uMsg,wParam,lParam );
}
  那么,接下来该怎么办呢?事实上,每个进程都有一个特殊的寄存器,它指向下一条要执行的指令的内存位置。即所谓的指令指针,在32位 Intel 和 AMD 处理器上被表示为 EIP。因为 EIP是一个专用寄存器,你无法象操作一般常规存储器(如:EAX,EBX等)那样通过编程存取它。也就是说没有操作代码来寻址 EIP,以便直接读取或修改其内容。但是,EIP 仍然还是可以通过间接方法修改的(并且随时可以修改),通过JMP,CALL和RET这些指令实现。下面我们就通过例子来解释通过 CALL/RET 子例程调用机制在32位 Intel 和 AMD 处理器上是如何工作的。
  当你调用(通过 CALL)某个子例程时,子例程的地址被加载到 EIP,但即便是在 EIP杯修改之前,其旧的那个值被自动PUSH到堆栈(被用于后面作为指令指针返回)。在子例程执行完时,RET 指令自动将堆栈顶POP到 EIP。
   现在你知道了如何通过 CALL 和 RET 实现 EIP 的修改,但如何获取其当前的值呢?下面就来解决这个问题,前面讲过,CALL PUSH EIP 到堆栈,所以,为了获取其当前值,调用“哑函数”,然后再POP堆栈顶。让我们用编译后的 NewProc 来解释这个窍门。
Address   OpCode/Params   Decoded instruction
--------------------------------------------------
:00401000 55 push ebp ; entry point of
; NewProc
:00401001 8BEC mov ebp, esp
:00401003 51 push ecx
:00401004 E800000000 call 00401009 ; *a* call dummy
:00401009 59 pop ecx ; *b*
:0040100A 83E909 sub ecx, 00000009 ; *c*
:0040100D 894DFC mov [ebp-04], ecx ; mov pData, ECX
:00401010 8B45FC mov eax, [ebp-04]
:00401013 83E814 sub eax, 00000014 ; pData--;
.....
.....
:0040102D 8BE5 mov esp, ebp
:0040102F 5D pop ebp
:00401030 C21000 ret 0010
  • 哑函数调用;就是JUMP到下一个指令并PUSH EIP到堆栈;
  • 然后将堆栈顶POP到 ECX,ECX再保存EIP;这也是 POP EIP指令的真正地址;
  • 注意 NewProc 的入口点和 “POP ECX”之间的“距离”是9 个字节;因此为了计算 NewProc的地址,要从 ECX 减9。

  这样一来,不管 NewProc 被移到什么地方,它总能计算出其自己的地址。但是,NewProc 的入口点和 “POP ECX”之间的距离可能会随着你对编译/链接选项的改变而变化,由此造成 RELEASE和DEBUG版本之间也会有差别。但关键是你仍然确切地知道编译时的值。

  1. 首先,编译函数
  2. 用反汇编确定正确的距离
  3. 最后,用正确的距离值重新编译

此即为 InjecEx 中使用的解决方案,类似于 HookInjEx,交换鼠标点击“开始”左右键时的功能。

方法二:

对于我们的问题,在远程进程地址空间中将 INJDATA 放在 NewProc 前面不是唯一的解决办法。看下面 NewProc的变异版本:

static LRESULT CALLBACK NewProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 消息标示符
WPARAM wParam, // 第一个消息参数
LPARAM lParam ) // 第二个消息参数
{
INJDATA* pData = 0xA0B0C0D0; // 虚构值

//-----------------------------
// 子类化代码
// ........
//-----------------------------

// 调用原来的窗口过程
return pData->fnCallWindowProc( pData->fnOldProc,
hwnd,uMsg,wParam,lParam );
}
  此处 0xA0B0C0D0 只是远程进程地址空间中真实(绝对)INJDATA地址的占位符。前面讲过,你无法在编译时知道该地址。但你可以在调用 VirtualAllocEx (为INJDATA)之后得到 INJDATA 在远程进程中的位置。 编译我们的 NewProc 后,可以得到如下结果:
 Address   OpCode/Params     Decoded instruction
--------------------------------------------------
:00401000 55 push ebp
:00401001 8BEC mov ebp, esp
:00401003 C745FCD0C0B0A0 mov [ebp-04], A0B0C0D0
:0040100A ...
....
:0040102D 8BE5 mov esp, ebp
:0040102F 5D pop ebp
:00401030 C21000 ret 0010
因此,其编译的代码(十六进制)将是:
558BECC745FCD0C0B0A0......8BE55DC21000.
现在你可以象下面这样继续:
  1. 将INJDATA,ThreadFunc和NewProc 拷贝到目标进程;
  2. 修改 NewProc 的代码,以便 pData 中保存的是 INJDATA 的真实地址。
    例如,假设 INJDATA 的地址(VirtualAllocEx返回的值)在目标进程中是 0x008a0000。然后象下面这样修改NewProc的代码:
    	558BECC745FCD0C0B0A0......8BE55DC21000 <- 原来的NewProc (注1) 
    558BECC745FC00008A00......8BE55DC21000 <- 修改后的NewProc,使用的是INJDATA的实际地址。
    也就是说,你用真正的 INJDATA(注2) 地址替代了虚拟值 A0B0C0D0(注2)。
  3. 开始执行远程的 ThreadFunc,它负责子类化远程进程中的控件。
  • 注1、有人可能会问,为什么地址 A0B0C0D0 和 008a0000 在编译时顺序是相反的。因为 Intel 和 AMD 处理器使用 little-endian 符号来表示(多字节)数据。换句话说,某个数字的低位字节被存储在内存的最小地址处,而高位字节被存储在最高位地址。
    假设“UNIX”这个词存储用4个字节,在 big-endian 系统中,它被存为“UNIX”,在 little-endian 系统中,它将被存为“XINU”。
  • 注2、某些破解(很糟)以类似的方式修改可执行代码,但是一旦加载到内存,一个程序是无法修改自己的代码的(代码驻留在可执行程序的“.text” 区域,这个区域是写保护的)。但仍可以修改远程的 NewProc,因为它是先前以 PAGE_EXECUTE_READWRITE 许可方式被拷贝到某个内存块中的。

何时使用 CreateRemoteThread 和 WriteProcessMemory 技术

  与其它方法比较,使用 CreateRemoteThread 和 WriteProcessMemory 技术进行代码注入更灵活,这种方法不需要额外的 dll,不幸的是,该方法更复杂并且风险更大,只要ThreadFunc出现哪怕一丁点错误,很容易就让(并且最大可能地会)使远程进程崩溃(参见附录 F),因为调试远程 ThreadFunc 将是一个可怕的梦魇,只有在注入的指令数很少时,你才应该考虑使用这种技术进行注入,对于大块的代码注入,最好用 I.和II 部分讨论的方法。

WinSpy 以及 InjectEx 请从这里下载源代码

结束语

到目前为止,有几个问题是我们未提及的,现总结如下:

解决方案 OS 进程
I、Hooks Win9x 和 WinNT 仅仅与 USER32.DLL (注3)链接的进程
II、CreateRemoteThread & LoadLibrary 仅 WinNT(注4) 所有进程(注5), 包括系统服务(注6)
III、CreateRemoteThread & WriteProcessMemory
 
仅 WinNT 所有进程, 包括系统服务
  • 注3:显然,你无法hook一个没有消息队列的线程,此外,SetWindowsHookEx不能与系统服务一起工作,即使它们与 USER32.DLL 进行链接;
  • 注4:Win9x 中没有 CreateRemoteThread,也没有 VirtualAllocEx (实际上,在Win9x 中可以仿真,但不是本文讨论的问题了);
  • 注5:所有进程 = 所有 Win32 进程 + csrss.exe
    本地应用 (smss.exe, os2ss.exe, autochk.exe 等)不使用 Win32 API,所以也不会与 kernel32.dll 链接。唯一一个例外是 csrss.exe,Win32 子系统本身,它是本地应用程序,但其某些库(~winsrv.dll)需要 Win32 DLLs,包括 kernel32.dll;
  • 注6:如果你想要将代码注入到系统服务中(lsass.exe, services.exe, winlogon.exe 等)或csrss.exe,在打开远程句柄(OpenProcess)之前,将你的进程优先级置为 “SeDebugPrivilege”(AdjustTokenPrivileges)。

  最后,有几件事情一定要了然于心:你的注入代码很容易摧毁目标进程,尤其是注入代码本身出错的时候,所以要记住:权力带来责任!
  因为本文中的许多例子是关于密码的,你也许还读过 Zhefu Zhang 写的另外一篇文章“Super Password Spy++” ,在该文中,他解释了如何获取IE 密码框中的内容,此外,他还示范了如何保护你的密码控件免受类似的攻击。

附录A

为什么 kernel32.dll 和user32.dll 总是被映射到相同的地址。

  我的假定:因为Microsoft 的程序员认为这样做有助于速度优化,为什么呢?我的解释是——通常一个可执行程序是由几个部分组成,其中包括“.reloc” 。当链接器创建 EXE 或者 DLL文件时,它对文件被映射到哪个内存地址做了一个假设。这就是所谓的首选加载/基地址。在映像文件中所有绝对地址都是基于链接器首选的加载地址,如果 由于某种原因,映像文件没有被加载到该地址,那么这时“.reloc”就起作用了,它包含映像文件中的所有地址的清单,这个清单中的地址反映了链接器首选 加载地址和实际加载地址的差别(无论如何,要注意编译器产生的大多数指令使用某种相对地址寻址,因此,并没有你想象的那么多地址可供重新分配),另一方 面,如果加载器能够按照链接器首选地址加载映像文件,那么“.reloc”就被完全忽略掉了。
  但kernel32.dll 和user32.dll 及其加载地址为何要以这种方式加载呢?因为每一个 Win32 程序都需要kernel32.dll,并且大多数Win32 程序也需要 user32.dll,那么总是将它们(kernel32.dll 和user32.dll)映射到首选地址可以改进所有可执行程序的加载时间。这样一来,加载器绝不能修改kernel32.dll and user32.dll.中的任何(绝对)地址。我们用下面的例子来说明:
  将某个应用程序 App.exe 的映像基地址设置成 KERNEL32的地址(/base:"0x77e80000")或 USER32的首选基地址(/base:"0x77e10000"),如果 App.exe 不是从 USER32 导入方式来使用 USER32,而是通过LoadLibrary 加载,那么编译并运行App.exe 后,会报出错误信息("Illegal System DLL Relocation"——非法系统DLL地址重分配),App.exe 加载失败。
为什么会这样呢?当创建进程时,Win 2000、Win XP 和Win 2003系统的加载器要检查 kernel32.dll 和user32.dll 是否被映射到首选基地址(实际上,它们的名字都被硬编码进了加载器),如果没有被加载到首选基地址,将发出错误。在 WinNT4中,也会检查ole32.dll,在WinNT 3.51 和较低版本的Windows中,由于不会做这样的检查,所以kernel32.dll 和user32.dll可以被加载任何地方。只有ntdll.dll总是被加载到其基地址,加载器不进行检查,一旦ntdll.dll没有在其基地址,进程就无法创建。

总之,对于 WinNT 4 和较高的版本中

  • 一定要被加载到基地址的DLLs 有:kernel32.dll、user32.dll 和ntdll.dll;
  • 每个Win32 程序都要使用的 DLLs+ csrss.exe:kernel32.dll 和ntdll.dll;
  • 每个进程都要使用的DLL只有一个,即使是本地应用:ntdll.dll;

附录B

/GZ 编译器开关

  在生成 Debug 版本时,/GZ 编译器特性是默认打开的。你可以用它来捕获某些错误(具体细节请参考相关文档)。但对我们的可执行程序意味着什么呢?
  当打开 /GZ 开关,编译器会添加一些额外的代码到可执行程序中每个函数所在的地方,包括一个函数调用(被加到每个函数的最后)——检查已经被我们的函数修改的 ESP堆栈指针。什么!难道有一个函数调用被添加到 ThreadFunc 吗?那将导致灾难。ThreadFunc 的远程拷贝将调用一个在远程进程中不存在的函数(至少是在相同的地址空间中不存在)

附录C

静态函数和增量链接

  增量链接主要作用是在生成应用程序时缩短链接时间。常规链接和增量链接的可执行程序之间的差别是——增量链接时,每个函数调用经由一个额外的JMP指令,该指令由链接器发出(该规则的一个例外是函数声明为静态)。这些 JMP 指令允许链接器在内存中移动函数,这种移动无需修改引用函数的 CALL指令。但这些JMP指令也确实导致了一些问题:如 ThreadFunc 和 AfterThreadFunc 将指向JMP指令而不是实际的代码。所以当计算ThreadFunc 的大小时:

const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc)
  你实际上计算的是指向 ThreadFunc 的JMPs 和AfterThreadFunc之间的“距离” (通常它们会紧挨着,不用考虑距离问题)。现在假设 ThreadFunc 的地址位于004014C0 而伴随的 JMP指令位于 00401020。
:00401020   jmp  004014C0
...
:004014C0 push EBP ; ThreadFunc 的实际地址
:004014C1 mov EBP, ESP
...
那么
WriteProcessMemory( .., &ThreadFunc, cbCodeSize, ..);
  将拷贝“JMP 004014C0”指令(以及随后cbCodeSize范围内的所有指令)到远程进程——不是实际的 ThreadFunc。远程进程要执行的第一件事情将是“JMP 004014C0” 。它将会在其最后几条指令当中——远程进程和所有进程均如此。但 JMP指令的这个“规则”也有例外。如果某个函数被声明为静态的,它将会被直接调用,即使增量链接也是如此。这就是为什么规则#4要将 ThreadFunc 和 AfterThreadFunc 声明为静态或禁用增量链接的缘故。(有关增量链接的其它信息参见 Matt Pietrek的文章“Remove Fatty Deposits from Your Applications Using Our 32-bit Liposuction Tools” )

附录D

为什么 ThreadFunc的局部变量只有 4k?

  局部变量总是存储在堆栈中,如果某个函数有256个字节的局部变量,当进入该函数时,堆栈指针就减少256个字节(更精确地说,在函数开始处)。例如,下面这个函数:

void Dummy(void) {
BYTE var[256];
var[0] = 0;
var[1] = 1;
var[255] = 255;
}
编译后的汇编如下:
:00401000   push ebp
:00401001 mov ebp, esp
:00401003 sub esp, 00000100 ; change ESP as storage for
; local variables is needed
:00401006 mov byte ptr [esp], 00 ; var[0] = 0;
:0040100A mov byte ptr [esp+01], 01 ; var[1] = 1;
:0040100F mov byte ptr [esp+FF], FF ; var[255] = 255;
:00401017 mov esp, ebp ; restore stack pointer
:00401019 pop ebp
:0040101A ret
  注意上述例子中,堆栈指针是如何被修改的?而如果某个函数需要4KB以上局部变量内存空间又会怎么样呢?其实,堆栈指针并不是被直接修改,而是通过另一个函数调用来修改的。就是这个额外的函数调用使得我们的 ThreadFunc “被破坏”了,因为其远程拷贝会调用一个不存在的东西。
  我们看看文档中对堆栈探测和 /Gs编译器选项是怎么说的:
——“/GS是一个控制堆栈探测的高级特性,堆栈探测是一系列编译器插入到每个函数调用的代码。当函数被激活时,堆栈探测需要的内存空间来存储相关函数的局部变量。
  如果函数需要的空间大于为局部变量分配的堆栈空间,其堆栈探测被激活。默认的大小是一个页面(在80x86处理器上4kb)。这个值允许在Win32 应用程序和Windows NT虚拟内存管理器之间进行谨慎调整以便增加运行时承诺给程序堆栈的内存。”
我确信有人会问:文档中的“……堆栈探测到一块需要的内存空间来存储相关函数的局部变量……”那些编译器选项(它们的描述)在你完全弄明白之前有时真的让 人气愤。例如,如果某个函数需要12KB的局部变量存储空间,堆栈内存将进行如下方式的分配(更精确地说是“承诺” )。
sub    esp, 0x1000    ; "分配" 第一次 4 Kb
test [esp], eax ; 承诺一个新页内存(如果还没有承诺)
sub esp, 0x1000 ; "分配" 第二次4 Kb
test [esp], eax ; ...
sub esp, 0x1000
test [esp], eax
  注意4KB堆栈指针是如何被修改的,更重要的是,每一步之后堆栈底是如何被“触及”(要经过检查)。这样保证在“分配”(承诺)另一页面之前,当前页面承诺的范围也包含堆栈底。

注意事项
  “每一个线程到达其自己的堆栈空间,默认情况下,此空间由承诺的以及预留的内存组成,每个线程使用 1 MB预留的内存,以及一页承诺的内存,系统将根据需要从预留的堆栈内存中承诺一页内存区域” (参见 MSDN CreateThread > dwStackSize > Thread Stack Size)
  还应该清楚为什么有关 /GS 的文档说在堆栈探针在 Win32 应用程序和Windows NT虚拟内存管理器之间进行谨慎调整。

现在回到我们的ThreadFunc以及 4KB 限制
  虽然你可以用 /Gs 防止调用堆栈探测例程,但在文档对于这样的做法给出了警告,此外,文件描述可以用 #pragma check_stack 指令关闭或打开堆栈探测。但是这个指令好像一点作用都没有(要么这个文档是垃圾,要么我疏忽了其它一些信息?)。总之,CreateRemoteThread 和 WriteProcessMemory 技术只能用于注入小块代码,所以你的局部变量应该尽量少耗费一些内存字节,最好不要超过 4KB限制。

附录E

为什么要将开关语句拆分成三个以上?

用下面这个例子很容易解释这个问题,假设有如下这么一个函数:

int Dummy( int arg1 ) 
{
int ret =0;

switch( arg1 ) {
case 1: ret = 1; break;
case 2: ret = 2; break;
case 3: ret = 3; break;
case 4: ret = 0xA0B0; break;
}
return ret;
}
编译后变成下面这个样子:
地址      操作码/参数       解释后的指令
--------------------------------------------------
; arg1 -> ECX
:00401000 8B4C2404 mov ecx, dword ptr [esp+04]
:00401004 33C0 xor eax, eax ; EAX = 0
:00401006 49 dec ecx ; ECX --
:00401007 83F903 cmp ecx, 00000003
:0040100A 771E ja 0040102A

; JMP 到表***中的地址之一
; 注意 ECX 包含的偏移
:0040100C FF248D2C104000 jmp dword ptr [4*ecx+0040102C]

:00401013 B801000000 mov eax, 00000001 ; case 1: eax = 1;
:00401018 C3 ret
:00401019 B802000000 mov eax, 00000002 ; case 2: eax = 2;
:0040101E C3 ret
:0040101F B803000000 mov eax, 00000003 ; case 3: eax = 3;
:00401024 C3 ret
:00401025 B8B0A00000 mov eax, 0000A0B0 ; case 4: eax = 0xA0B0;
:0040102A C3 ret
:0040102B 90 nop

; 地址表***
:0040102C 13104000 DWORD 00401013 ; jump to case 1
:00401030 19104000 DWORD 00401019 ; jump to case 2
:00401034 1F104000 DWORD 0040101F ; jump to case 3
:00401038 25104000 DWORD 00401025 ; jump to case 4
注意如何实现这个开关语句?

  与其单独检查每个CASE语句,不如创建一个地址表,然后通过简单地计算地址表的偏移量而跳转到正确的CASE语句。这实际上是一种改进。假设你有50个CASE语句。如果不使用上述的技巧,你得执行50次 CMP和JMP指令来达到最后一个CASE。相反,有了地址表后,你可以通过表查询跳转到任何CASE语句,从计算机算法角度和时间复杂度看,我们用O(5)代替了O(2n)算法。其中:
  1. O表示最坏的时间复杂度;
  2. 我们假设需要5条指令来进行表查询计算偏移量,最终跳到相应的地址;

  现在,你也许认为出现上述情况只是因为CASE常量被有意选择为连续的(1,2,3,4)。幸运的是,它的这个方案可以应用于大多数现实例子中,只有偏移量的计算稍微有些复杂。但有两个例外:

  • 如果CASE语句少于等于三个;
  • 如果CASE 常量完全互不相关(如:“"case 1” ,“case 13” ,“case 50” , 和“case 1000” );

  显然,单独判断每个的CASE常量的话,结果代码繁琐耗时,但使用CMP和JMP指令则使得结果代码的执行就像普通的if-else 语句。
有趣的地方:如果你不明白CASE语句使用常量表达式的理由,那么现在应该弄明白了吧。为了创建地址表,显然在编译时就应该知道相关地址。

现在回到问题!
注意到地址 0040100C 处的JMP指令了吗?我们来看看Intel关于十六进制操作码 FF 的文档是怎么说的:

操作码 指令     描述
FF /4  JMP r/m32  Jump near, absolute indirect,
           address given in r/m32

  原来JMP 使用了一种绝对寻址方式,也就是说,它的操作数(CASE语句中的 0040102C)表示一个绝对地址。还用我说什么吗?远程 ThreadFunc 会盲目地认为地址表中开关地址是 0040102C,JMP到一个错误的地方,造成远程进程崩溃。

附录F

为什么远程进程会崩溃呢?

当远程进程崩溃时,它总是会因为下面这些原因:

  1. 在ThreadFunc 中引用了一个不存在的串;
  2. 在在ThreadFunc 中 中一个或多个指令使用绝对寻址(参见附录E);
  3. ThreadFunc 调用某个不存在的函数(该调用可能是编译器或链接器添加的)。你在反汇编器中可以看到这样的情形:
    :004014C0    push EBP         ; ThreadFunc 的入口点
    :004014C1 mov EBP, ESP
    ...
    :004014C5 call 0041550 ; 这里将使远程进程崩溃
    ...
    :00401502 ret
    如果 CALL 是由编译器添加的指令(因为某些“禁忌” 开关如/GZ是打开的),它将被定位在 ThreadFunc 的开始的某个地方或者结尾处。

  不管哪种情况,你都要小心翼翼地使用 CreateRemoteThread 和 WriteProcessMemory 技术。尤其要注意你的编译器/链接器选项,一不小心它们就会在 ThreadFunc 添加内容。

参考资料

- 作者: EmZoo 2009年08月20日, 星期四 10:33  回复(0) |  引用(0) 加入博采

[内核注入]之交换ID[可怕的特性]
摘要:交换ID过后,如果关闭此进程则殃及自己 查看全文

- 作者: EmZoo 2009年08月20日, 星期四 10:26  回复(0) |  引用(0) 加入博采

利用代码注入脱壳
摘要:脱壳 查看全文

- 作者: EmZoo 2009年08月20日, 星期四 10:24  回复(0) |  引用(0) 加入博采

牢记邓小平的一句话:到了21世纪,中国就强大了
摘要:有人讲过一个故事说,上世纪80年代中,尼克松访华演讲中提出了一个重要观点,认为即使没有苏联的因素,中美关系到了下个世纪仍然十分重要。当时陪同在座的中国外交部长吴学谦觉得这话有点意思,便向邓小平作了汇报。没想到邓小平听后毫无反应,却说出了一句意味深长的话:“到了21世纪,中国就强大了。” 查看全文

- 作者: EmZoo 2008年04月18日, 星期五 09:45  回复(0) |  引用(0) 加入博采

How to Debug Applets in Java Plug-in

How to Debug Applets in Java Plug-in



This section covers the following topics:

Debugging Java applets in Java Plug-in has not been simple in the past, partly because the applets use many services and facilities from the Java 2 Runtime Environment, Java Plug-in, and the browser itself. If the applet does not work, the developer needs to spend time diagnosing the problem.

The purpose of this document is to simplify the debugging process. It provides techniques and suggestions for developing applets in Java Plug-in and describes some common mistakes in applet development.

How to Debug Applets in Java Plug-in

In order to debug applets, you must have the appropriate version of the JDK installed on your machine. Also make sure to compile your .java files with -g option with javac. To begin debugging your applet:

  1. Start the Java Control Panel and go to the Java tab.
  2. In the Java Applet Runtime Settings area, click View. The Java Runtime Settings dialog box opens.
  3. Specify the following parameters:
    -Djava.compiler=NONE
    -Xnoagent
    -Xdebug
    -Xrunjdwp:transport=dt_shmem,address=
    <connection-address>,server=y,suspend=n

    The <connection-address> could be any string which is used by the java debugger later to connect to the JVM. For example,

    -Djava.compiler=NONE
    -Xnoagent
    -Xdebug
    -Xrunjdwp:transport=dt_shmem,address=2502,server=y,suspend=n

    See JPDA Connection and Invocation for the details on the possible runtime parameters for debugging.

  4. Start Internet Explorer or Netscape Navigator and load the page which contains the applet to be debugged. Make sure the applet code has been compiled with -g option with javac.
  5. Run the command jdb -attach <connection address> in a DOS command prompt. <connection address> is the name mentioned in the step 1. For example, if <connection address> is 2502, you will run the command as
    		  jdb -attach 2502
    To learn more about the Java Debugger (jdb), see The Java Debugger.
  6. Once the jdb has attached to the VM, you can set up breakpoints in the applet.
  7. When the applet in the browser reaches the breakpoint, it will stop executing, and you will see the debugger waiting for your input to continue debugging.

When debugging applets in Java Plug-in, make sure that only one instance of the browser is being used for debugging using the same connection address at the same time. Otherwise, it will result in a conflict, since the Java Runtime for each instance of the browser will try to gain exclusive access to the connection address. To debug applets in both Internet Explorer and Netscape Navigator, run either Internet Explorer or Netscape Navigator with Java Plug-in—but not both at the same time.

Debugging applets in Java Plug-in with Active Desktop is discouraged because an instance of Internet Explorer will always be running in the desktop process during the lifetime of the user session.

You can use other Java 2 debuggers, like Inprise's JBuilder or Symantac's VisualCafe, instead of jdb. To use these debuggers, you will need to change the project option in these IDEs to attach Java Plug-in in the browser process on the same machine or remote machine. Different Java Runtime Parameters may also be required in the Java Control Panel. For more information, consult your Java 2 debugger or IDE manuals.

Java Plug-in Console

One of the most powerful tools in Java Plug-in is the Java Plug-in Console. It is a simple console window for redirecting all the System.out and System.err messages. The console window is disabled by default; it can be enabled from the Java Control Panel or the task bar. If the console is enabled, you will see the console window appear when Java Plug-in is used in the browser.

Java Plug-in Trace File

Similar to the Java Plug-in Console, this is a file that records all the System.out and System.err messages. The trace file is disabled by default but is automatically enabled when the Java Plug-in Console is enabled. The trace file is normally located in user.dir, and the file is called .plugin<version>.trace. For example, in Windows NT this file is located in C:\WINNT\Profiles\<username>\.plugin<version>.trace.

javaplugin.trace property

This property controls whether Java Plug-in prints its trace messages during execution. This is useful to applet developers to determine what is occuring within Java Plug-in. The possible values are true or false. By default this property is false. To enable this property, set -Djavaplugin.trace=true in the Java Runtime Parameters field accessed from the Java tab of the Java Control Panel.

java.security.debug property

This property controls whether the security system of the Java 2 Runtime Environment prints its trace messages during execution. This is usful when a security exception is thrown in an applet or when a signed applet is not working. The following options are supported:

  • access   — print all checkPermission results
  • jar         — print jar verification information
  • policy   — print policy information
  • scl         — print permissions SecureClassLoader assigns

The following options can be used with access:

  • stack     — include stack trace
  • domain   — dumps all domains in context
  • failure — before throwing exception, dump the stack and domain that didn't have permission

For example, to print all checkPermission results and dump all domains in context, set -Djava.security.debug=access:stack in the Java Runtime Parameters dialog box access from the Java tab of the Java Control Panel.

Documentation

Java Plug-in provides a rich set of documentation to help developers use the various features of Java Plug-in. The documentation includes a FAQ, which includes some of the most frequently asked questions by developers. Make sure you read and understand these documents before applet development, as it may save you hundreds of hours in debugging.

Isolating Bugs

Although Java Plug-in provides the Java 2 Runtime Environment within Internet Explorer and Netscape Navigator, most of the facilities are provided by the Java 2 Runtime itself, rather than by Java Plug-in. Therefore, if a problem occurs in Java Plug-in, it may be either a problem in Java Plug-in, the Java 2 Runtime itself or a user error. It is extremely important to determine where bugs originate, as it will affect the speed of bug evaluation and fixing. Here are some suggestions for isolating bugs:

  1. Run the applets in both Internet Explorer and Netscape Navigator through Java Plug-in. 
  2. Run the applets in appletviewer. Java Plug-in is mainly derived from appletviewer and has inherited problems from appletviewer as well. This step should be performed only if the applet doesn't require specific browser facilities that Java Plug-in provides, like HTTPS or RSA signing.
  3. If the applet fails in appletviewer, it is likely the problem is in the Java 2 Runtime Environment—and not in Java Plug-in. 
  4. If the applet fails in only one of the browsers, IE or Netscape, it is likely a Java Plug-in problem.
  5. If the applet fails in both browsers but not appletviewer, it could be either a Java Plug-in problem or user error. Please examine the applet code to see if it makes any assumptions about the execution environment. For example, in appletviewer the current directory is set to the current directory in the shell when appletviewer is launched, whereas the current directory in Java Plug-in may be set to the browser's directory. Therefore, loading resources from the current directory may work in appletviewer but not in Java Plug-in.
  6. Try to reproduce the problem on other machines or platforms. In some cases, the root of the problem may be in the machine configuration, e.g., an improper DNS setup.
  7. If you have identified the problems in the Java 2 Runtime Environment or Java Plug-in, please follow the instructions in the next section to submit a bug report to the appropriate product categories.

Submitting Bug Reports

To submit a bug report, go to the Java Development Connection's BugParade. Before submitting a bug, search the BugParade to determine if the bug has already been reported. In some cases, a workaround may also have been suggested. If the bug is not already reported, submit a new bug report to the Java Plug-in team. In the bug report, include the following information:

  • Complete description of the problem and step-by-step instructions for reproducing it;
  • Error messages captured by Java Plug-in Console or trace file;
  • Proxy configuration information, e.g., auto proxy configuration with proxy configuration file attached;
  • Browser and platform information, e.g., Navigator 4.7 on Win 2000;
  • A test case demonstrating the problem;
  • Specify whether the problem occurs in other browsers and appletviewer;
  • Specify any workaround available;
  • Specify personal information—your name and email address—so you may be contacted if additional information is required.

Submitting Feature Requests

To submit a feature request, do so through the Report A Bug or Request a Feature page.In the feature request, please make sure the following information is included:

  • Complete description of the requested feature;
  • How this feature will improve the quality of your product or Java Plug-in in general.

Java Plug-in Feedback Alias

The purpose of the Java Plug-in Feedback alias, java-plugin-feedback@sun.com, is for customers to provide feedback on product features and the product in general. This alias is not intended for bug report submission. To submit a bug report, please follow the instructions given above.

 

- 作者: EmZoo 2007年11月1日, 星期四 15:04  回复(0) |  引用(0) 加入博采

DIY资料FTP地址公布
!!!DIY资料FTP地址公布!!!(更新)

ftp://cect@ftp.arima.com.cn
用户名:cect
密码:ldcs333333

在第3个乱码文件夹内有个名字叫 DIY资料 的文件夹,
里面是我搜集的一点资料,有几百M,大家可以用FTP软件去连接,也可以直接用IE去连接。
欢迎大家补充资料。
补充资料统一放在第3个乱码文件夹内,方便管理。



!!珍贵的外文电子制作杂志FTP服务器!!本论坛locky_z提供!


ftp://locky-z.vicp.net:3021/

里面大部分是国外(英文、俄文、法文、西班牙、德文都有)电子杂志和Application Note文档,都已经转换成PDF,还有一些是合订本的ISO文件,共有14G资料。以后还有共6G的元件的PDF文件和收集的电路上传。看十年也看不完
但这个ftp服务器只支持ftp的port方式,对于那些独立的ADSL就没问题,但通过电视宽带或者共享上网的就可能不能下载。最大200个用户,每个用户4线程,每线程20K。最好用下载软件下,直接IE下好像定时会断开。我试过flashGet没问题。


具体文件列表看这里:http://www.erji.net/read.php?tid=227022&page=e#a



但是要记得一点:做人要厚道,资料要看不要删。服务器是人家的,别搞破坏。

- 作者: EmZoo 2007年03月16日, 星期五 13:20  回复(0) |  引用(0) 加入博采

上海社保之粗略定量分析

上海社保之粗略定量分析

发工资了,看着工资条上被扣四金的数目,心中一动,不知道自己和单位所缴纳的金额对以后会有多少的保险效果,所以上网查了一些资料,作了以下的粗略定量分析。由于资料均google自网上,分析过程中的假设前提也是一时之想,因此不保证任何的真实性和有效性,仅为个人自娱自乐尔。
?
社保包括多种保险,比较常见的有四种:养老保险、失业保险、医疗保险、住房公积金。
?
根据上海规定,假如个人工资超过上一年度的上海职工平均工资三倍的话,以三倍平均工资作为缴费基数;否则就以个人工资数目作为缴费基数。
养保、失保和医保每年四月份对缴费基数进行调整,当前缴费基数的最大值是6705元;公积金每年七月份对缴费基数进行调整,当前缴费基数的最大值为6099元。(2006年4月份的数据)
?
根据规定的缴费比率乘以相应的缴费基数,个人工资部分会被首先扣除四金,而单位也需要缴纳相应的配套部分。具体比率各不相同,详见下文说明。
?
http://www.12333.gov.cn/index.shtml输入个人身份证号码可以知道养保、失保、医保的一些缴费信息。
http://ybj.sh.gov.cn/webquery/search01.jsp输入身份证号码可以知道医保的简单信息。
http://www.shgjj.com/加上身份证号码和公积金号码可以查询住房公积金的情况。
?
一. 养老保险
个人8%,单位22%(有些资料上说22.5%不知道哪个是准确的)
“目前我国企业职工养老保险个人账户主要由“单位缴费”与“个人缴费”共同组成,目前全国各地单位缴费的标准并不一样,上海的“单位缴费”比例为工资的22%”
?
个人部分进个人帐户,单位部分被国家统筹。
(以前是个人8%和单位3%进个人帐户,单位的其余部分被统筹,在去年年底的时候出了规定说从2006年开始单位的全部都被统筹,没有在网上找到正式通知启用新制度,姑且先按照新制度来计算吧)
根据最新政策,退休时的养老保险金的计算公式为:
退休时的养老金=基础养老金+个人帐户养老金

其中,"个人帐户养老金"来自个人帐户,"基础养老金"来自统筹部分。

?

"个人帐户养老金"的大概规则(是否享受利息不清楚)为:

假设在x岁退休,当地平均寿命为y岁,退休时个人帐户中已累积有z元(未承诺利率水平)

那么,这个部分每月可以领取 z/((y-x)*12)元。

按照当前标准,基数为6705,个人每月交536.4,那么连续交30年后,假设60岁退休,平均寿命是80,那么每月可以领取536.4*12*30/20/12 = 804.6。按照2%的年复利(姑且不论目前社保基金的年收益),每个月可领取536.4*12*(1-1.02^30)/(1-1.02)/20/12=1088,每个月可以多领取约25%,这还是不计领取保险金期间的收益的情况下。

所以,个人帐户到底可以享受如何的利率水平很值得关注。

?

再来看统筹部分的规则:

基础养老金=(当地上年度在岗职工月平均工资 + 本人指数化月平均工资)/2*(缴费年数/100)
?其中,本人指数化月平均缴费工资 = 当地上年度在岗职工月平均工资 * 本人缴费指数; “本人缴费指数”为“本人当月缴费工资”除“当月当地在岗职工月平均工资”后的历年算术平均值。
?
假设现在上海平均月工资为a,根据上海规定,我们缴纳四金的基数为3a(当前标准即6705),所以,30年的缴费后,我们可以得到统筹部分的养老金为:
(a+3a)/2 * 0.3 = 0.6a元/月
?
而单位每月要缴纳(3a)*22%=0.66a,也就是说每月交0.66a,交30年;退休后每月可以领取0.6a,领20年。考虑每年2%的复利,那具体的差额是:
0.66a*12*(1.02^30-1)/0.02 - 0.6a*12*20 = 177.3a
按照当前标准a=6705/3
差额达到39.6w,可真够吓人一跳的。
?
?
?而如果平均工资按照每年3%的速度上升,还是考虑2%的年收益复利,那么差额是:
487.7935a - 39.1329a = 448.66a = 100w,假如这钱能够支援个人买房的话,那也可以居者有其屋了......
?
有些怀疑自己算错了,以下是具体过程:
第一年 缴费0.66a*12,?? ??? 30年后有 0.66a*12*1.02^29
?? 二????? 0.66a*12*1.03???????29???????????? 0.66a*12*1.03*1.02^28
?? 三????? 0.66a*12*1.03^2?????????????????? 0.66a*12*1.03^2*1.02^27
...
?? 30???? 0.66a*12*1.03^29???????????????? 0.66a*12*1.03^29
计有0.66a*12*(首项是1.02^29,公比是1.03/1.02,共30项的等比数列之和)=487.79a
?
而领取时:
第一年??? 0.6a*1.03^30
?? 2?????????? 0.6a*1.03^31
...
? 20????????? 0.6a*1.03^49
计有0.6a*1.03^30*(首项是1,公比1.03,共20项的等比数列之和)=39.13a
?
领取退休金的20年期间,已缴费的收益不计。
?
在matlab中运行以下代码,可以看到平均工资的年增长率在0%到16%时的情况(年复利收益还是2%)。
clear;
clc;
x=[];
y=[];
z=[];
b=1.02;
for p=0.00:0.002:0.16???
??? if (p+1==b | p==0)
??????? continue;
??? end
???
??? i=p+1;
??? x=[x p*100];
??? m=0.66*12*b^29*(1-(i/b)^30)/(1-i/b) - 0.6*(i)^30*(1-i^20)/(1-i);???????????????????????
??? m=m*(6705/3.0)/10000;
??? y=[y m];
??? z=[z 0];
end
plot(x,y);
xlabel('平均工资年增长率(%)');
ylabel('差额(万元人民币)');
hold on;
plot(x,z,'k');
?
从图中可以看出差额一开始随着平均工资增长率的增加而不断增加,在约11%时达到最高点217.52w,然后开始下降,在约14.8%时差额接近0,然后差额开始出现负数。
假如年复利收益超过2%的话,那么上述的两个百分比数字可能要再向x轴正方向偏移,差距也将增大。
?
?
二. 失业保险
上海市失业保险办法(99)7号
个人交1%,单位交2%
“失业人员领取失业保险金的期限,根据其失业前累计缴纳失业保险费的年限(扣除已领取失业保险金的缴纳失业保险费年限)计算。累计缴纳失业保险费满1年不满2年的,领取失业保险金的期限为2个月;累计缴纳失业保险费年限每增加1年,期限增加2个月。累计缴纳失业保险费满1年不满5年的,期限最长为12个月;累计缴纳失业保险费满5年不满10年的,期限最长为18个月;累计缴纳失业保险费10年以上的,期限最长为24个月。 ”
“失业人员第1个月至第12个月领取的失业保险金标准,根据其缴纳失业保险费的年限确定;第13个月至第24个月领取的失业保险金标准,为其第1个月至第12个月领取标准的80%。失业保险金标准应当低于本市当年最低工资标准、高于本市当年城镇居民最低生活保障标准。 ”
"失业人员在领取失业保险金期间生育或者患病的,可以在户籍所在地的地段医院或者指定的医疗机构就诊,并可以向就业服务机构申请领取医疗补助金。"
?
????? 看上去好像是这样的,不管你交了多少失业保险金,最多让你领取两年,而且,领取的金额是由交费年限决定的。按照当前的标准,基数是6705,每月个人交67.1,单位交134.2,假如将钱存银行而且不计算利息的话,那么10年后,按照最长24个月计算,每月可以提取(67.1+134.2)*12*10/24 = 1006.5,假如考虑长期定期或货币基金和复利因素,那么每月提取数目将更多。很是怀疑领取的失业保险金能否达到1k,还在学校的时候听说杭州的最低生活保障标准是300元/月。
?
????? 所以,感觉”失业保险“中”保险“两字,并无平日所理解的”保险“之要义,不如改名为”失业救济基金",更加的顾名思义,调剂社会资源向弱势倾斜。
?
?
?
三. 医疗保险
“用人单位应当按其缴费基数10%的比例缴纳基本医疗保险费,并按其缴费基数2%的比例缴纳地方附加医疗保险费”
个人2%,单位12%。
个人部分进入个人医疗帐户,单位部分被政府统筹。以下医疗过程中只有被纳入医保范畴的才被医疗保险所覆盖,其他药品和手术都是自行负责的。
?
对门急诊,首先使用个人医疗帐户中的金额,用完后,全部自负。
如果是住院或者门诊大病等,当费用超过一定限度后,被政府统筹的钱终于起作用了,会支付医药费的80%多,剩下的自负。(不知道剩下的部分能否先使用个人医疗帐户中的钱?)
?
????? 以上只是说的非常粗陋,根据在职与退休、年龄变化、疾病种类等,政策都有不同的差异,差异主要体现在自负的比率的不同。更多资料,参考http://ybj.sh.gov.cn/webquery/ybZlservice_05.jsp?(不知道这个网页中提到的个人帐户部分, 上一年度平均工资的0.5%的钱是由谁出的?)
?
????? 评价一下,假如只是小病门急诊的话,用的是个人医疗帐户中的钱,其实就是自己的钱,预先从工资中扣到个人医疗帐户中而已(好像还没有利息的?)。不过假如住院或者大病的话,那还是起到保险意义的。不过的是,只是对医保范畴的保险,范围相当狭窄,所以商业保险中的大病保险业务需求相当的大,经过前段时间对重疾病保险的关注后发现商业保险中猫腻很多,所以就等着保监会出台重大疾病保险的规范然后去买一点了,呵呵,我还是很相信政府的。
????? 按照目前的标准,基数是6705,假如将这些钱存银行,不考虑利息,那么10年后计有(134.1+804.6)*12*10=112644,按照每年2%的复利计算可以达到12.3w,而在职期间医保最多支付85%,12.3/85%=14.5w。根据现在商业保险的宣传,一次大病的费用在10w-20w之间,考虑到医保具有可持续性,所以到底存银行还是医保哪个更加对个人有利,就看这10年内的住院和大病发生情况了。仁者见仁智者见智,不过我感觉,每10年发生一次以上的大病或者相当多次的住院,其概率是非常小的。
?
?
综上所述,我们目前是在为国家的社保政策做贡献,本来也无可厚非,和谐社会就是需要国家政策能够将社会资源部分的重新分配,倾向弱势群体。不过,看上去,上位者似乎疏于去调查取富济贫中到底哪些人是富哪些人是贫。看经济要素,除了看名义收入外,姑且不论那些隐性的福利,还要看所拥有的实物财产,比如房产等等。所以,我们只是虚高的工资,可怜政策却拿着我们虚高的工资开刀,部分支援着那些虚低工资而家产富足的群体。我们也是需要政策倾斜的弱势群体。
所以,对个人来说,落袋为安也是正道。
??
以上的三种保险均属于上海的城镇社会保险,所谓'城保"
另有上海市小城镇社会保险,简称“镇保",暂不在本文讨论范围内。
?
?
四. 住房公积金
?个人7%,单位7%
就随它去吧,目前没有资格能够用上它。
?
?

- 作者: EmZoo 2007年03月12日, 星期一 10:55  回复(0) |  引用(0) 加入博采

RTTI解释
RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息。它提供了运行时确定对象类型的方法。本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI;本文还将详细描述两个重要的 RTTI 运算符的使用方法,它们是 typeid 和 dynamic_cast。
其实,RTTI 在C++中并不是什么新的东西,它早在十多年以前就已经出现了。但是大多数开发人员,包括许多高层次的C++程序员对它并不怎么熟悉,更不用说使用 RTTI 来设计和编写应用程序了。
一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用 RTTI 机制。但是,在很多情况下,虚拟函数无法克服本身的局限。每每涉及到处理异类容器和根基类层次(如 MFC)时,不可避免要对对象类型进行动态判断,也就是动态类型的侦测。如何确定对象的动态类型呢?答案是使用内建的 RTTI 中的运算符:typeid 和 dynamic_cast。
首先让我们来设计一个类层次,假设我们创建了某个处理文件的抽象基类。它声明下列纯虚拟函数:open()、close()、read()和 write():

class File
{
public:
virtual int open(const string & filename)=0;
virtual int close(const string & filename)=0;
//
virtual ~File()=0; // 记住添加纯虚拟析构函数(dtor)
};
现在从 File 类派生的类要实现基类的纯虚拟函数,同时还要提供一些其他的操作。假设派生类为 DiskFile,除了实现基类的纯虚拟函数外,还要实现自己的flush()和defragment()操作: class DiskFile: public File
{
public:
int open(const string & filename);

// 实现其他的纯虚拟函数
......

// 自己的专有操作
virtual int flush();
virtual int defragment();
};
接着,又从 DiskFile 类派生两个类,假设为 TextFile 和 MediaFile。前者针对文本文件,后者针对音频和视频文件: class TextFile: public DiskFile
{
// ......
int sort_by_words();
};

class MediaFile: public DiskFile
{
//......
};
我们之所以要创建这样的类层次,是因为这样做以后可以创建多态对象,如:File *pfile; // *pfile的静态类型是 File
if(some_condition)
pfile = new TextFile; // 动态类型是 TextFile
else
pfile = new DiskFile; // 动态类型是 DiskFile
假设你正在开发一个基于图形用户界面(GUI)的文件管理器,每个文件都可以以图标方式显示。当鼠标移到图标上并单击右键时,文件管理器打开一个菜单,每个文件除了共同的菜单项,不同的文件类型还有不同的菜单项。如:共同的菜单项有“打开”“拷贝”、和“粘贴”,此外,还有一些针对特殊文件的专门操作。比如,文本文件会有“编辑”操作,而多媒体文件则会有“播放”菜单。为了使用 RTTI 来动态定制菜单,文件管理器必须侦测每个文件的动态类型。利用 运算符 typeid 可以获取与某个对象关联的运行时类型信息。typeid 有一个参数,传递对象或类型名。因此,为了确定 x 的动态类型是不是Y,可以用表达式:typeid(x) == typeid(Y)实现:#include <typeinfo> // typeid 需要的头文件
void menu::build(const File * pfile)
{
if (typeid(*pfile)==typeid(TextFile))
{
add_option("edit");
}
else if (typeid(*pfile)==typeid(MediaFile))
{
add_option("play");
}
}
使用 typeid 要注意一个问题,那就是某些编译器(如 Visual C++)默认状态是禁用 RTTI 的,目的是消除性能上的开销。如果你的程序确实使用了 RTTI,一定要记住在编译前启用 RTTI。使用 typeid 可能产生一些将来的维护问题。假设你决定扩展上述的类层次,从MediaFile 派生另一个叫 LocalizeMedia 的类,用这个类表示带有不同语言说明文字的媒体文件。但 LocalizeMedia 本质上还是个 MediaFile 类型的文件。因此,当用户在该类文件图标上单击右键时,文件管理器必须提供一个“播放”菜单。可惜 build()成员函数会调用失败,原因是你没有检查这种特定的文件类型。为了解决这个问题,你必须象下面这样对 build() 打补丁: void menu::build(const File * pfile)
{

//......

else if (typeid(*pfile)==typeid(LocalizedMedia))
{
add_option("play");
}
}
唉,这种做法真是显得太业余了,以后每次添加新的类,毫无疑问都必须打类似的补丁。显然,这不是一个理想的解决方案。这个时候我们就要用到 dynamic_cast,这个运算符用于多态编程中保证在运行时发生正确的转换(即编译器无法验证是否发生正确的转换)。用它来确定某个对象是 MediaFile 对象还是它的派生类对象。dynamic_cast 常用于从多态编程基类指针向派生类指针的向下类型转换。它有两个参数:一个是类型名;另一个是多态对象的指针或引用。其功能是在运行时将对象强制转换为目标类型并返回布尔型结果。也就是说,如果该函数成功地并且是动态的将 *pfile 强制转换为 MediaFile,那么 pfile的动态类型是 MediaFile 或者是它的派生类。否则,pfile 则为其它的类型:void menu::build(const File * pfile)
{
if (dynamic_cast <MediaFile *> (pfile))
{
// pfile 是 MediaFile 或者是MediaFile的派生类 LocalizedMedia
add_option("play");
}
else if (dynamic_cast <TextFile*> (pfile))
{
// pfile 是 TextFile 是TextFile的派生类
add_option("edit");
}
}
细细想一下,虽然使用 dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响。
参考资料:http://www.vckbase.com/document/viewdoc/?id=653

- 作者: EmZoo 2007年03月12日, 星期一 09:26  回复(0) |  引用(0) 加入博采

SoftICE (DriverStdio)下载地址
http://pool.upsdn.net/debug


01.18.02-NuMega.DriverStudio.v2.6-iPF,   2002/05/27,   4K  
    ftp://ftp.seu.edu.cn/Pub/Develope/SDK_DDK_DRV/Numega.DriverStudio/  
      NuMega   DriverStudio   2.6   Examples.msi,   2002/06/06,   1.2M  
    ftp://202.114.22.9/Pub/待处理文件/Programming/Tools/Debug/SoftICE   Driver   Suite/Setup/  
      01.18.02-NuMega.DriverStudio.v2.6-iPF,   2002/06/07,   0  
    ftp://srd.ftpserver.biz/soft/Develope/SDK_DDK_DRV/Numega.DriverStudio/  
      numega.driverstudio.v2.6-ipf.nfo,   2002/03/21,   10.4K  
    ftp://202.38.78.13/pub/programming/driverstudio/  
      numega.driverstudio.v2.6-ipf.nfo,   2002/01/19,   10.4K  
    ftp://202.38.74.13/pub/Software/_=ByCompany=_/NuMega/NuMega.DriverStudio.v2.6-iPF/  
      NuMega.DriverStudio.v2.6-iPF,   2002/03/03,   512  
    ftp://202.38.74.13/pub/Software/_=ByCompany=_/NuMega/  
      numega.driverstudio.v2.6-ipf.nfo,   2002/01/20,   10.5K  
    ftp://210.35.168.11/pub1/Programming/Debugger/Numega.Driver.Studio.v2.6.WinALL.Incl.Keymaker-CORE/  
      NuMega   DriverStudio   2.6.msi,   2002/01/22,   5.0M  
    ftp://166.111.136.2/pub/Develop/SDK/DriverStudio/2.6/Setup/  
      numega.driverstudio.v2.6-ipf.nfo,   2002/01/22,   10.5K  
    ftp://166.111.136.2/pub/Develop/SDK/DriverStudio/2.6/  
      NuMega   DriverStudio   2.6   Examples.msi,   2002/01/22,   1.2M  
    ftp://166.111.136.2/pub/Develop/SDK/DriverStudio/2.6/Setup/  

- 作者: EmZoo 2006年12月30日, 星期六 11:29  回复(0) |  引用(0) 加入博采

MYSQL使用原则
摘要:与MySQL交互时,应用程序应使用该一般性原则 查看全文

- 作者: EmZoo 2006年12月25日, 星期一 09:37  回复(0) |  引用(0) 加入博采

Kernel System Calls
摘要:This article is the first step towards an understanding of how kHTTPd can take the role of a web server while never leaving kernel space. 查看全文

- 作者: EmZoo 2006年12月21日, 星期四 17:42  回复(0) |  引用(0) 加入博采

Write a Linux Hardware Device Driver
摘要:Learn to write and install a Linux device driver to control a hardware card. 查看全文

- 作者: EmZoo 2006年12月21日, 星期四 17:07  回复(0) |  引用(0) 加入博采

VoIP通讯协议及4大术语
摘要:关于VoIP设备的产品规格术语,其实相当地多,但如果抓住4大必记术语,就可以将市面上的各种VoIP设备作基本的分类。 查看全文

- 作者: EmZoo 2006年12月21日, 星期四 10:17  回复(0) |  引用(0) 加入博采

StarDict词库
摘要:下面是Stardict的词库: http://ftp2.ua.freebsd.org/pub/FreeBSD/distfiles/stardict/ 需要更好的服务可以去stardict的网站。 查看全文

- 作者: EmZoo 2006年12月1日, 星期五 11:44  回复(0) |  引用(0) 加入博采

编译FC5内核源代码
摘要:安装kernel的src.rpm 到:http://download.fedora.redhat.com/pub/fedora/linux/core/5/source/SRPMS/kernel-2.6.15-1.2054_FC5.src.rpm下载 查看全文

- 作者: EmZoo 2006年12月1日, 星期五 11:42  回复(0) |  引用(0) 加入博采

visual slickedit 11 11.0.1 11.0.2的破解
摘要:SlickEdit for Linux的破解. 查看全文

- 作者: EmZoo 2006年11月28日, 星期二 15:01  回复(0) |  引用(0) 加入博采

博客价值评估软件
摘要:万象嵌入式资源站 评估价值:10,950 RMB。 查看全文

- 作者: EmZoo 2006年11月23日, 星期四 16:14  回复(0) |  引用(0) 加入博采

哈哈,终于在Linux下成功驱动D-Link DWL G-122.
摘要:激动人心的PING 查看全文

- 作者: EmZoo 2006年09月18日, 星期一 11:46  回复(0) |  引用(0) 加入博采

Linux下面的socket编程,如何获得本机IP地址
摘要:Linux下面的socket编程,如何获得本机IP地址 查看全文

- 作者: EmZoo 2006年09月13日, 星期三 13:36  回复(0) |  引用(0) 加入博采

ViewML交叉编译

ViewML : Open source web browser for embedded linux system

就是寫Microwindow的作者,的另一個計畫(?)。

他發現雖然opensource 的internet browser雖然已經有20幾種,但是大多很大,或是需要其他package。要不然就是功能太陽春,無法parse複雜的HTML tag。
所以他決定要作一套新的,可以用在embedded system,而且功能又OK的internet browser。
他決定要:
  1. 一套完整的internet browser,但是又很小,可以在很多embedd device中執行(像 pda, set-top-box。
  2. 使用現成的open source html parse engine,以免像其自行撰寫prase engine的browser一樣,prase能力很差。
  3. 使用Fast Light Toolkit(FLTK) framework 作為user interface。
  4. 可以在microwindow和X windows下執行。
所以他選了 KDE 的KFM使用的KDE1.0 HTML widget。因為他小,而且提供的功能已經購完整。至於Qt library這一點,將會由FLK 重新implement一次。

ViewML 分為以下module :

ViewML Browser Application Layer
用FLTK寫的application user interface。包裝起整個ViewML程式。處理user interface和network/local file access 介面。

W3C WWWLib
負責asychronous network i/o 和HTTP get function。這是WWW 協會的標準WWWLib。有點大,將來可能會自己重寫。

KHTML View/Widget
就是KDE 1.0 HTML Widget code。KHTML Widget 負責parse HTML,繪圖,但是scroll 則交由View去作。

Qt Compatiblity Layer
用FTLK 將所有KHTML用到的Qt function call寫出來,讓KHTML能夠不修改code,正常的執行。不直接拿Qt來用的原因是size問題。

IMLIB image library
gnome的image library。包含自動偵測image file format,decodeC 和show image。

FLTK Application Framework
有兩種版本: 給Win32, X 用的標準版FLTK,和給microwindow的nano-X用的重新寫過的FLTK。

因為利用了open source,完成的ViewML browser的code size只有800k,run time memory requirement也只有2M。整體,加上mircrowindow,只需要2.5M。
整個project是open source的。

project page在 www.viewml.com (但是已經變成pilix了)

freshmeat的ViewML也只有到0.21版。
真正的ViewML位置在PILIX ftp download site上 (ftp://ftp.pixil.org/pub/pixil)< >看來原作者很不爽繼續支援這個計畫,ViewML目前附屬在PIXIL 計畫(產品)下。在PIXIL 的developer mailing list上可以看到作者已經無意free support這個計畫。-- 甚至port 到new compiler version也不願意。對於一大堆大陸人的(Is anyone help me cross compile viewML for arm) 問題也不願回應。



build ViewML :

需要的library有
一般:
  • STL (這個好像是用STLport)
  • PThread (uclibc 有libpthread)
  • libwww - 去ftp download,同時需要libmd5
for nano-X
  • FLTK for Nano-X , flnx-current.tar.gz
  • Microwindows , with nano-X
準備好以上的package後...
解開viewml,在目錄中run .confiure,並且加入參數
./configure  --with-microwin=[Microwindows dir]  --with-fltk=[FLTK dir] --host=[prefix]
Microwindows dir 是microwindws 所在目錄
FLTK 也是一樣
prefix是toolchain的prefix,像arm-elf



libwww

不知道要怎樣在uclinux 下build。

紀錄一下cross build的一些option.
ARMTOOLSPREFIX = arm-linux-

libwww的installation 下有說明
Specifying the System Type

There may be some features configure can not figure out automatically, but needs to determine by the type of host the package will run on. Usually configure can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields:

CPU-COMPANY-SYSTEM

See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type.

If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package.
config.sub 是...,看了後,好像要設arm-linux :
./configure --target=arm-linux
build 出來後,看看Makefile,好像沒又設對gcc。所以用:<>CC=arm-elf-gcc ./configure --target=arm-linux卻看到 cannot find host,所以host也要指定。因為不知道要指定那一種所以用./configure看看...host system type... i686-pc-linux-gnu。
這次用
CC=arm-elf-gcc ./configure --target=arm-linux --host=i686-pc-linux-gnu
試試..但是output中checking for gcc 還是gcc,不是arm-elf-gcc,所以check cross-compiler 還是no.
$ echo ${CC-cc}
arm-elf-gcc
這個是正確的。
是config.cache的關係嗎?
把w3c-libwww整個delete掉,untar,再configure一次,OK !!正確detect出cross compile了。
接著作
$ make
Error :
Making all in xmltok
arm-elf-gcc -shared -Wl,-soname -Wl,libxmltok.so.0 -o .libs/libxmltok.so.0.1.0 xmlrole.lo xmltok.lo -lc
/home/charles/sigma/armutils_2.5.91.0/toolchain/lib/gcc-lib/arm-elf/2.95.3/libc.a(__uClibc_main.o): In function `__uClibc_start_main':
__uClibc_main.o(.text+0xf8): undefined reference to `main'
collect2: ld returned 1 exit status
是share library的問題?
改configure, disable share :
./configure --host=i686-pc-linux-gnu --prefix=~/libwww --disable-shared 2>&1 | tee output
OK!! 所以應該是shared library的問題。
因為ViewML沒有說一定要用shared library,所以先用static linking試試。

... make是過了,但是還沒install.
這樣的configure option 不能install 吧,因為沒設prefix,所以會裝在default folder裏(/usr/lib)。

...其實前一次會過是因為沒有設CXX=arm-elf-g++ 變數,所以checking variable size時都會OK。
使用:
 CC=arm-elf-gcc CXX=arm-elf-g++ ./configure  --host=i686-pc-linux-gnu --target=arm-linux --prefix=/home/charles/magsi/libwww --disable-shared 2>&1 | tee output
就會發生...
configure: error: can not run test program while cross compiling
Google的結果,這一篇似乎有一個解決的方法...
這是因為autoconfig沒有辦法知道target platform的char size,所以發出error(autoconfig利用run test program來偵測machine的var size)。
解覺的方法就是手動囉,因為只有build的人才會知道taget system的char size。
改configure...找到 echo "configure: error: can not run test program while cross compiling" 這一行,參考一下這一段是要 設定那一個變數,自己寫進去,改為:
ac_cv_sizeof_char=1
找一下所有這個message出現的地方,修改對應參數到正確的值...
因為資質弩鈍,所以有些type不知道,像arm的long type size是....?
所以只好真的build一個test program在taget board 上跑來試試..
使用的command是
arm-elf-gcc trysize.c -o trysize -Wl,-elf2flt="-s32768"
分別印出
char 1
char* 4
int 4
long 4
還要check time_t 是不是long : 就是time_t 的size和long 一不一樣
double 和long double是不是一樣: 寫給target board test的code是:

#include
#include
#include

int isbigendian(void)
{
union
{
long l;
char c[sizeof(long)];
} u;
u.l=1;

return u.c[sizeof(long) -1] == 1;
}

int charisunsigned(void)
{
volatile char c= 255;
return c <0;
}
main()
{
printf("char:%d\n",sizeof(char));
printf("char*:%d\n",sizeof(char*));
printf("int:%d\n",sizeof(int));
printf("long:%d\n",sizeof(long));
printf("time_t:%d\n",sizeof(time_t));
printf("size_t:%d\n",sizeof(size_t));
printf("bigendian:%d\n",isbigendian());
printf("charisunsigned:%d\n",charisunsigned());
printf("double:%d\n",sizeof(double));
printf("long double:%d\n",sizeof(long double));

}

輸出的結果是:
char:1
char*:4
int:4
long:4
time_t:4
size_t:4
bigendian:0
charisunsigned:0
double:8
long double:8
實際上還是不知道long_double這一個選項是要設成yes還是no...







FTLK

一樣,解開。 README中說明INSTALL process:有說明configure時如果不是用g++,要指定CXX variable。gcc要指定CC variable。
$ CXX=arm-elf-g++
$ CC=arm-elf-gcc
$ ./configure --host=i686-pc-linux-gnu --preifx=/home/charles/libflnx
OK。正確check出cross compile。
$ make 
Error !!!
=== making fluid ===
make[1]: Entering directory `/home/charles/sigma/flnx/fluid'
.....
make[1]: *** No rule to make target `../lib/libfltk.a', needed by `fluid'. Stop.
make[1]: Leaving directory `/home/charles/sigma/flnx/fluid'
=== making test ===
make[1]: Entering directory `/home/charles/sigma/flnx/test'
make[1]: *** No rule to make target `../lib/libfltk.a', needed by `valuators'. Stop.
make[1]: Leaving directory `/home/charles/sigma/flnx/test'
make: *** [all] Error 2
這個....
應該要看一下flnx的INSTALL..

和libwww的方式不一樣,fltk 下的Makefile包含了config,所以直接在Makefile裏修改,加入configure的option。

./configure --host=i686-pc-linux-gnu --with-microwin=/home/charles/magsi/armutils_2.5.91.0/build_arm/src/microwin/src
但是還是一樣的錯。

真奇怪,不就是要build libfltk嗎?為什麼不知道要如何build libfltk ?
這是因為libfltk.a 是由src 目錄build出來,src目錄有錯。所以libfltk沒有build出來。

所以要先查src目錄make的錯誤...
....是找不到iostream.h...
設定-I option 到makeinclude 中
-I/home/charles/magsi/armutils_2.5.91.0/build_arm/STLport-4.5.3/stlport 
OK。但是出現putenv( ) undeclare!!!。
這 是因為stdlib.h include不正確,一樣,在剛剛的-I option 之前,要指定uclibc 的include 路徑,讓arm-elf-g++先去include uclibc的stdlib.h,所以makeinclude的-I option 要加上另一項(在STLport option 之前...
-I/home/charles/sigma/armutils_2.5.91.0/build_arm/uClibc -0.9.26/include
再build.. 還是有錯,說是filename_list.cxx中有一個cast不正確。到 source file中去看。是platform dependent code。在uclinux下應該要用linux 的code吧,需要define "linux",所以再到makeinclude中加入..
-Dlinux
OK!! src folder build OK.

然後是fluid folder的問題了....
internal error--unrecognizable insn:
Google 一下...
Hit the WALL !!!
原來是cross-compile 太複雜的C++ code時,會出現的error,mailinglist上有所謂的"unrecognuzabgle-insn.path",但是都是失效的link !

有兩個解決方法:
1.upgrade gcc
2.修改code

修改code好了@_@...
所以把出現error的function找出來,mark 掉一些section,找找看是那一個instruction發生問題..
發現...竟然是local variable array 的size太大!!!
把 MAXSIZE 改小就可以,--- 改到256後,pixmap_image::pixmap_image 沒錯。
--- 也試過用new 也可以過,但是要注意 return 時要free。

改為gif.cxx出錯!!
gif.cxx 裡面只有一個function : gif2xpm,因為error message的內容和上一個有一點類似,所以猜是不是local array size too large,果然找到-- 有兩個4096 的array,改成100後OK。

fluid 都build過了,link時出現"cannot find -ljpeg". 猜測是沒有jpeg library,在viewml的ftp裏好像有看到...jpegsrc.v6b.tar.gz,google一下發現這是有名的jpeg-6b 版本library。
有關cross-compile的也只有... ./configure ,修改compile to sh8-linux-gcc (是sh8的說明)。
解開source 後,裡面有install.doc,

...好像uClibc也有port libjpeg... not sure-- 結果沒有
將libjpeg cross build好 - ref http://checko.blogspot.com/2006/02/cross-compile-libjpeg.html
繼續build flnx...

compile 都過了,剩下link error : 找不到rint( ) - 屬於libm。
雖然uClibc中有libm(否則link會complain: 找不到libm),但是卻找不到rint( ),
自己寫一個測試程式用cross-compile build試試,結果真的找不到。
到lib,用
$ arm-elf-ar t libm
列出libm中所有module,沒有s_rint.o <-這是rint()所在module。
所以查一下uClibc的Makefile為何沒有把s_rint( )納入...
在uClibc-0.9.26/libm/Makefile中看到 因為定義 DO_C99_MATH沒定義,所以沒將s_rint.c加入source list。
所以手動將s_rint.c加入source list中,重新build uClibc (因為make 下有uClibc.mk,所以打 make uclibc 就可以只build uclibc)
完成後再用" arm-elf-ar t libm" 看,s_rint.o 已經在libm中。

回到flnx, make --- fluid 已經OK,
出現test folder 的error : 一樣是 " keyboard_ui.cxx:559: internal error--unrecognizable insn: "

看了一下,大概是button太多了,所以刪掉所有的button.
build OK!!
應該可以測試一下...
在target上.... 不會跑 : bad magic/rec,
這個是沒有作elf2flt的原因,
修改 Makefile,在 linker, loader rule加上 :
-Wl,-elf2flt="-s32768"
.rebuild,注意exe file要chmod a+x 才能執行。
# posted by CheckoBlog : 2:26 下午 0 comments links to this post

- 作者: EmZoo 2006年08月21日, 星期一 17:41  回复(0) |  引用(0) 加入博采

Microwindows在基于单片机嵌入式系统中的移植
摘要:icrowindows是由Gregory Haerr组织的一个开放源码项目,是嵌入式系统中广泛应用的一种图形用户接口(GUI),该项目的目标是在嵌入式Linux平台上提供与普通个人电脑类似的图形用户界面。 查看全文

- 作者: EmZoo 2006年08月21日, 星期一 09:29  回复(0) |  引用(0) 加入博采

Framebuffer编程How-to
摘要:注:本文是Console programming HOWTO,Wiebe Zoon ;wiebe@linvision.com的一部分,原文为英文版,本文由highbar翻译。如转载,请注明原作者及译者。 查看全文

- 作者: EmZoo 2006年08月17日, 星期四 09:53  回复(0) |  引用(0) 加入博采

准备移植一个Nano + viewml到EM8621L上面(原创)

ftp://ftp.pixil.org/pub/pixil/

ftp://microwindows.censoft.com/pub/microwindows/

http://www.microwindows.org/Nano-XTutorial.html

http://www.pixil.org

http://www.edaboard.com/forum90.html

嵌入式GUI

Microwindows由Century Software的CEO Greg Haerr主持开发的一个公开源码(LGPL)的项目。Microwindows致力于为一些小型设备和平台提供现代图形窗口环境。Microwindows支持许多硬件平台,移植性很强。Microwindows的主要目的之一便是运行在嵌入式Linux上,并且提供了基于Win32/X的两套API接口。
http://www.microwindows.org
http://microwindows.org

MiniGUI由原清华大学教师魏永明先生开发,是中国人做的得较好的自由软件之一。MiniGUI 是一种面向嵌入式系统或者实时系统的公开源码(LGPL)的图形用户界面支持系统。它主要运行于Linux控制台,实际可以运行在任何一种具有POSIX线程支持的POSIX兼容系统上。
http://www.minigui.org
飞漫软件的MiniGUI项目
http://www.minigui.com/company/cindex.shtml

Qt/Embedded是著名的QT库开发商Trolltech正在进行的面向嵌入式系统的QT版本。Qt/Embedded对于各种硬件接口到GUI工具包提供了完整的图形栈。Qt/Embedded的API同Qt/X11和Qt/Windows的相同,但它并不是基于X11库的。Qt/Embedded是公开源码(LGPL)项目。
http://www.trolltech.com

OpenGUI基于一个用汇编实现的x86图形内核,提供了一个快速的、32位的、高层的C/C++图形接口。OpenGUI也是一个公开源码(LGPL)项目。OpenGUI提供了二维绘图原语,消息驱动的API和BMP文件格式支持。
http://www.tutok.sk/fastgl

PicoGUI是一个可以工作在包括手持式设备等各种硬件上的小型的、可移植的、基于客户/服务器结构的GUI。同X Window系统一样,它具有客户—服务器结构的灵活性,但又不同于X Window系统,它将字体、BMP文件、控件以及一些应用程序所需要的其它的一些资源直接集成在服务器。虽然减少了系统的灵活性,但在速度上有了很大的提高,并且减小了程序大小。
http://picogui.org

Tiny-X是一个为嵌入式系统而开发的紧缩型的X Window服务器。它由SuSE赞助,由XFree86的核心成员Keith Packard开发。Tiny-X的目标是可以在小内存或几乎无内存的情况下良好运行。
http://www.pps.jussieu.fr/~jch/software/kdrive.html

PIXIL提供嵌入式在高级因特网中应用程序的应用。它虽然是为商业化准备的,但它提供GPL协议下的版本,区别是没有技术支持。
http://www.pixil.org

NxZilla - Mozilla on NanoX
http://nxzilla.sourceforge.net

Simple DirectMedia Layer is a cross-platform multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer. It is used by MPEG playback software, emulators, and many popular games, including the award winning Linux port of "Civilization: Call To Power."
http://www.libsdl.org/index.php

GtkFB: GTK+ for the Linux Framebuffer
http://www.linuxdevices.com/articles/AT9024868021.html

- 作者: EmZoo 2006年08月17日, 星期四 01:21  回复(0) |  引用(0) 加入博采