1.MessageBox()
int MessageBox(
HWND hWnd, //A handle to the owner window of the message box
LPCTSTR lpText, //The message to be displayed.
LPCTSTR lpCaption,//The dialog box title
UINT uType //The contents and behavior of the dialog box
);
注意:第四个参数如果有多个值,可以用或操作符"|"连接,如 MB_OK | MB_ICONSTOP
文档:https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-messagebox
MessageBoxW()和MessageBoxA()和MessageBox()的关系:
MessageBox()实际上是一个宏,根据传入的是Unicode字符或ASCII字符自动调用MessageboxW()和MessageBoxA()
下面的文章从RING3追踪到RING0,深入解析了MessageBox()的工作原理,很有认真学习一遍的价值,最好自己跟踪一次。读完后深感自己的水平之低,遂努力学习。
深入理解MessageBox():https://blog.csdn.net/AcceZn/article/details/54670776
自己写一个MessageBox()按照上面的方法跟踪学习一下:
#include <Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, PSTR szCmdLine, int iCmdshow)
{
MessageBox(NULL, TEXT("Hello world"), TEXT("Lesson1"), MB_OK);
return 0;
}
选择Release模式编译一下,Debug模式的话会大很多,调试起来不方便看
下面的数据库也保留,这次主要目的不是学习逆向分析,主要是跟踪一下MessageBox()的调用流程。
放入OD,给MessageBox()下一个断点:bp MessageBoxW 然后把程序跑起来,停在了MessageBoxW的函数入口:
发现确实就是简单地把参数传入MessageBoxTimeoutW(),步入继续追踪
下面就进入到了文章中说到的给MSGBOXDATA结构体赋值的过程,这个成员有点多,就没有一一分析了。根据文章所说,下面将会用这个结构体作为唯一的参数调用一个user32.dll未导出的函数–MessageBoxWorker。那么未导出函数在OD里面是什么样子的呢?又找到一篇文章:https://www.cnblogs.com/gussing/archive/2009/08/18/1548869.html 文章嵌套文章感觉自己更菜了,读完了只知道未导出函数就只能看到一个call 地址。按照正常流程,应该看MessageBoxTimeoutW()如何给MSGBOXDATA结构体赋值,然后可以给这个结构体下个内存断点或者直接看它作为哪一个call的参数。
这里偷懒了,后面讲到MessageBoxWorker()调用了SoftModalMessageBox()这个导出函数。所以给这个函数下个断点,断下来看它返回在哪个函数,说明这个函数就是MessageBoxWorker()。
最后发现
76021340 |. E8 58F3FFFF call user32.7602069D
这个函数就是MessageBoxWorker()
在这个函数中找到了调用的NtUserModifyUserStartupInfoFlags()
这个未导出函数不知道为什么被OD识别出来了,可能是逆向中用得比较多 。
最后MessageBoxWorker()调用了SoftModalMessageBox()弹出了对话框。
这样就大致地跟踪了一遍MessageBox()的调用流程。其中还有很多不足的地方,比如说两大流程(参考《MessageBox深入研究》)只跟踪了一边,对NtUserModifyUserStartupInfoFlags()在内核中的一系列操作也没有弄清楚,这些都是需要进一步学习的。
2.ASCII&UNICODE的处理
以前对字符集和字符编码没有搞清楚。只知道ACII是7/8位,UNICODE分为UTF-X,X=8/16/32等分别有8/16/32位。原理知道了,具体实现不知道,一般都是报错了或者出了乱码,然后改一下,当然这种态度是不好的,所以认真学习了一下在标准C中和Windows编程中是如何对字符集进行处理的。
#include <stdio.h>
int main()
{
char str[] = "哈哈哈";
printf("% s % c", str, str[1]);
return 0;
}
可以看到打印字符串成功了,字符失败。因为根据ANSI编码规则,编译器自动将字符串作为了系统的默认语言简体中文输出,而中文是两个字节表示的,所以用%c打印一个字符当然失败了。这个程序如果在非中文的系统上运行,字符串打印出来就也是乱码。 同时ANSI还带来了一系列衍生问题,如字符串长度计算问题。
因此,提出了UNICODE编码。为了方便使用,Windows 编程中,使用 TEXT() 将字符串括起来可以解决编码问题。使用 TCHAR 来定义字符及字符串。在宽字符的环境里,Windows 自动将 TCHAR 替换为 C 语言的 wchar_t 类型,TEXT() 自动在字符串前加 L 标识。
由此我们也可以知道C语言中的处理办法,其实就是用wchat_t声明一个宽字节字符变量,在字符串前加L将其变成宽字节模式。