手把手教你怎么用AI仿写没有源码的程序

最近同事大叔的手机上的邮箱密码忘记了,或者说他从来没记过密码,要我帮他找回,还好他电脑上有登录以前的邮箱软件,软件设置里面有密码,但是显示星号,并不能直接看到这个密码,如图:

所以我们需要可以显示星号密码的软件,把密码还原出来。一番搜索,找到一个可用的软件如图:

原文链接:分享一款Windows神器-*星号密码查看器-腾讯云开发者社区-腾讯云 (tencent.com)

 下载链接如下: https://www.lanzoui.com/i2tcmej

 使用效果如下:

但是现在微软在win10中把这个软件识别为病毒,并不能下载下来,不过在win8以下都不会报毒,好好的工具,但是win10不能使用,真的很闹心,当然微软可能考虑这种工具容易泄露个人密码,当然了,我们今天不是来讨论微软的对错,我不是要让大家使用这个方法用在不良目的之上,就像微软的想法一样,但是我们自己都不知道密码了,还需要这个密码有什么用处那?

既然win10不能使用这个软件,我们何不自己逆向出这个软件的核心代码,并通过AI帮我们生成仿照的代码(这里我再次申明,请不要把这个方法用在不良目的上,一切法律问题,还是要你自己承担的),不说废话,我们既然要把这个软件逆向,少不了IDA和X64dbg这样的调试工具,所以先用IDA加载分析程序(本人使用的是IDA7.6),等待IDA分析完成之后,即左下角显示AU: idle就是分析完成了,然后找到左侧的Functions(函数窗口-里面是IDA分析出来的所有函数),如下图:

找到创建对话框的wWinMain主函数,双击这个函数名,右侧的反汇编窗口就会显示wWinMain函数,如下图:

可以看到上面的红框处的连续5个push汇编代码,就是传入DialogBoxParamW的参数,这个函数的具体定义和用法见如下文字: 

DialogBoxParamW 函数是 Windows API 的一部分,特别用于 C 或 C++ 语言编写的 
Win32 API 编程。它用于从对话框模板资源创建模态对话框。以下是其基本概述:

函数原型:该函数声明如下:

c
Copy code
INT_PTR DialogBoxParamW(
    HINSTANCE hInstance,
    LPCWSTR   lpTemplateName,
    HWND      hWndParent,
    DLGPROC   lpDialogFunc,
    LPARAM    dwInitParam
);
hInstance:包含对话框模板的模块的句柄。
lpTemplateName:对话框模板的名称。
hWndParent:拥有该对话框的窗口的句柄。
lpDialogFunc:指向对话框程序的指针。
dwInitParam:指定传递给对话框的额外初始化数据。
字符编码:DialogBoxParamW 函数名末尾的 'W' 表明该函数使用宽字符(Unicode)。还有一个用于 ANSI 字符的 DialogBoxParamA 版本。

使用方式:要使用此函数,你需要一个对话框模板(通常在资源文件中定义)和一个定义对话框行为的对话框程序函数。

返回值:它返回一个 INT_PTR,通常用于指示对话框的结果(如 IDOK 或 IDCANCEL)或指示错误。

错误处理:如果函数失败,它将返回 -1。可以使用 GetLastError 函数获得详细的错误信息。

典型用途:它通常用于传统 Windows 桌面应用程序,用于创建和管理对话框,如设置面板、输入表单等。

显然lpDialogFunc这个参数传入的DialogFunc,就是窗口的消息处理函数,所有的功能也是这个函数实现或调用的,我们可以双击上图蓝框中的offset  DialogFunc定位到这个函数,如下图:

这个函数可能会有点长,而且有很多跳转,我们在这个文本查看模式,看这个代码不习惯,我们可以按空格切换到消息框查看模式,如下图:

在进入这个模式后,按住左键能拖到消息框,也可以按CTRL + 鼠标中键放大缩小,我们可以看看缩小的程序规模,如下图:

 

你可能看到上面的消息框有点懵,这么多怎么看那?其实你只要从上到下的浏览一遍就可以了,而且上面的一些消息框都比较小,显然都是一些跳转语句,关键查看中间和下面的比较长的的消息框,在看过所有的消息框后,你就会对程序的流程有一个大致的了解,根据我的观察,那些offset后的字符串和API函数能提示你很多信息,比如下图:

 

在上图中,我们可以看到红框里的Edit和Internet Explorer_Server,显然这都是程序在判断窗口的类名,程序在这几个和接下来引线到达的几个消息框里处理并解密了星号密码。可能你觉得消息框模式你看着不舒服,那我们可以使用IDA的伪代码模式,先再次按空格切换到文本查看模式,然后按F5,这样IDA就会打开一个新窗口,显示的是刚刚DialogFunc函数的伪代码(这里我要说句题外话,大佬略过,如果要学习调试汇编程序,建议去看看罗云彬的 《Windows环境下32位汇编语言程序设计》这本书,里面对你看懂反汇编很有用,看完后再去学习汇编、C++等语言会有新的认知),不说废话了,看如下伪代码:

INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM nResult)
{
  HICON IconW; // eax
  HDC DC; // esi
  HDC v6; // edi
  HWND v7; // esi
  LONG WindowLongW; // edi
  LPARAM *v9; // eax
  int v10; // eax
  POINT v12; // [esp-8h] [ebp-268h]
  struct tagPOINT Point; // [esp+Ch] [ebp-254h] BYREF
  int v14; // [esp+14h] [ebp-24Ch] BYREF
  struct tagPOINT pt; // [esp+18h] [ebp-248h] BYREF
  struct tagPAINTSTRUCT Paint; // [esp+20h] [ebp-240h] BYREF
  WCHAR v17[12]; // [esp+60h] [ebp-200h] BYREF
  WCHAR lParam[120]; // [esp+78h] [ebp-1E8h] BYREF
  WCHAR ClassName[122]; // [esp+168h] [ebp-F8h] BYREF

  if ( a2 > 0x200 )
  {
    if ( a2 == 513 )
    {
      v12.y = HIWORD(nResult);
      v12.x = (unsigned __int16)nResult;
      if ( PtInRect(&rc, v12) && !dword_40D9A4 )
      {
        ShowWindow(::hWnd, 0);
        hCursor = SetCursor(dword_40D9A8);
        SetCapture(hWnd);
        dword_40D9A0 = hWnd;
        dword_40D9A4 = 1;
      }
    }
    else if ( a2 == 514 && dword_40D9A4 )
    {
      dword_40D9A4 = 0;
      if ( dword_40D980 )
      {
        (*(void (__stdcall **)(int))(*(_DWORD *)dword_40D980 + 8))(dword_40D980);
        dword_40D980 = 0;
      }
      if ( dword_40D99C )
      {
        sub_401360(dword_40D9A0);
        dword_40D99C = 0;
      }
      ShowWindow(::hWnd, 5);
      SetCursor(hCursor);
      ReleaseCapture();
    }
  }
  else
  {
    switch ( a2 )
    {
      case 0x200u:
        if ( dword_40D9A4 )
        {
          //下面这句是通过GetCursorPos得到当前鼠标的坐标
          GetCursorPos(&Point);
          wsprintfW(v17, L"%d, %d", Point.x, Point.y);
          SendDlgItemMessageW(hWnd, 1002, 0xCu, 0, (LPARAM)v17);
          //下面这句是通过WindowFromPoint来确定给定点的屏幕坐标下最上层窗口的句柄
          v7 = WindowFromPoint(Point);
          if ( !v7 || dword_40D9A0 == v7 )
          {
            if ( dword_40D980 )
            {
              ScreenToClient(v7, &Point);
              v9 = (LPARAM *)sub_401160(&v14, dword_40D980, Point.x, Point.y);
              SendDlgItemMessageW(hWnd, 1004, 0xCu, 0, *v9);
              v10 = v14 - 16;
              if ( _InterlockedDecrement((volatile signed __int32 *)(v14 - 16 + 12)) <= 0 )
                (*(void (__stdcall **)(int))(**(_DWORD **)v10 + 4))(v10);
            }
          }
          else
          {
            if ( dword_40D99C )
            {
              sub_401360(dword_40D9A0);
              dword_40D99C = 0;
            }
            dword_40D9A0 = v7;
            GetWindowRect(v7, &stru_40D98C);
            stru_40D98C.right -= stru_40D98C.left;
            stru_40D98C.bottom -= stru_40D98C.top;
            stru_40D98C.left = 0;
            stru_40D98C.top = 0;
            sub_401360(v7);
            dword_40D99C = 1;
            #下面这句是通过GetClassNameW获得类名
            if ( GetClassNameW(v7, ClassName, 120) )
            {
              lParam[0] = 0;
              if ( lstrcmpiW(ClassName, L"Edit") || (GetWindowLongW(v7, -16) & 0x20) == 0 )//这句通过对比,上面GetClassNameW获得的类名是否是'Edit'来判断是否是密码框
              {
                if ( lstrcmpiW(ClassName, L"Internet Explorer_Server") )
                //上面这句是判断类名是不是IE窗口的,以下的代码都是处理IE窗口密码的,实际我使用下来
                //对很多网页并没有作用
                {
                  if ( dword_40D980 )
                  {
                    (*(void (__stdcall **)(int))(*(_DWORD *)dword_40D980 + 8))(dword_40D980);
                    dword_40D980 = 0;
                  }
                  SendMessageW(v7, 0xDu, 0x78u, (LPARAM)lParam);
                }
                else
                {
                  dword_40D980 = sub_401080(v7);
                  lstrcpyW(lParam, "騗蹚eQI");
                }
              }
              else
              //'else'下面的代码都是操作Edit类的,也就是我们的密码框的
              {
                WindowLongW = GetWindowLongW(v7, -16);
                SetWindowWord(v7, -16, 0);
                SetWindowLongW(v7, -16, WindowLongW & 0xFFFFFFDF);
                SendMessageW(v7, 0xDu, 0x78u, (LPARAM)lParam);
                SetWindowLongW(v7, -16, WindowLongW);
              }
             //下面这句是把密码显示给大家看的
              SendDlgItemMessageW(hWnd, 1004, 0xCu, 0, (LPARAM)lParam);
            }
          }
        }
        break;
      case 0xFu:
        BeginPaint(hWnd, &Paint);
        EndPaint(hWnd, &Paint);
        break;
      case 0x10u:
        EndDialog(hWnd, nResult);
        break;
      case 0x110u:
        IconW = LoadIconW(hInstance, (LPCWSTR)0x66);
        SendMessageW(hWnd, 0x80u, 0, (LPARAM)IconW);
        ::hWnd = GetDlgItem(hWnd, 1003);
        DC = GetDC(hWnd);
        v6 = GetDC(::hWnd);
        GetDCOrgEx(DC, &pt);
        GetDCOrgEx(v6, &Point);
        ReleaseDC(hWnd, DC);
        ReleaseDC(::hWnd, v6);
        GetClientRect(::hWnd, &rc);
        rc.right += Point.x - pt.x;
        rc.left = Point.x - pt.x;
        rc.bottom += Point.y - pt.y;
        rc.top = Point.y - pt.y;
        dword_40D9A8 = LoadCursorW(hInstance, (LPCWSTR)0x68);
        break;
    }
  }
  return 0;
}

 如上面我在伪代码中注释的,程序先通过GetCursorPos得到当前鼠标的坐标,然后使用WindowFromPoint来确定给定点的屏幕坐标下最上层窗口的句柄,接着GetClassNameW获得类名,通过'Edit'来判断是否是密码框,接着还有IE窗体的判断和处理代码(IE这部分的代码,我并没有使用成功,具体的原因有点复杂,等到下次我再分析,这次只做Edit类的获取方式),所以最关键的就是下面的代码:

WindowLongW = GetWindowLongW(v7, -16);//获取窗口的窗口样式
SetWindowWord(v7, -16, 0);//把窗口样式的数值清空,即把*号样式清空
SetWindowLongW(v7, -16, WindowLongW & 0xFFFFFFDF);//清除扩展窗口样式的第6位(win32con.ES_PASSWORD取反)
SendMessageW(v7, 0xDu, 0x78u, (LPARAM)lParam);//使用WM_GETTEXT消息获取窗口文本内容
SetWindowLongW(v7, -16, WindowLongW);//还原窗口的窗口样式

为了实际验证,是否是这些代码起作用,我们用x64dbg动态调试看看,实际调试过程我就不罗嗦了,主要看上面的"SendMessageW(v7, 0xDu, 0x78u, (LPARAM)lParam);"这句,图中的"push eax"就是参数lParam字符串指针,也就是实际获得的密码字符串,看下面两张图:

正如上面的代码,当然了,如果你使用c++编译代码也能通过,但是大概率也是要被微软检测到,所以我们可以使用Python,但是我们不知道怎么编写,这时我们可以请出我们的gpt来帮我们实现,先把有用的伪代码整理出来,告诉gpt,让它给我们编写python版本即可。

GetCursorPos(&Point);
wsprintfW(v17, L"%d, %d", Point.x, Point.y);
v7 = WindowFromPoint(Point);
if ( GetClassNameW(v7, ClassName, 120) )
{
   if ( lstrcmpiW(ClassName, L"Edit") || (GetWindowLongW(v7, -16) & 0x20) != 0
   {
	WindowLongW = GetWindowLongW(v7, -16);
        SetWindowWord(v7, -16, 0);
        SetWindowLongW(v7, -16, WindowLongW & 0xFFFFFFDF);
        SendMessageW(v7, 0xDu, 0x78u, (LPARAM)lParam);
        SetWindowLongW(v7, -16, WindowLongW);
    }
}

以下是gpt的回答:

虽然代码能用,但是并不是很完美,我个人感觉永远不要百分百相信机器,它只是一个工具,以下是我修改后的代码,我添加了循环语句让程序能不停检测,只要把鼠标移动到上面,密码就会在程序列表里显示出来(按ctrl+c可以结束程序),同时会在这个python脚本的同目录里生成一个"password.log"的文件,里面有获得过的密码。

代码如下:

import ctypes
import win32con
import keyboard
import time

window_style = 0
logArr = []

def getPassWrd():
    while True:
        time.sleep(0.25)
        # 获取鼠标指针的当前位置
        point = ctypes.wintypes.POINT()
        ctypes.windll.user32.GetCursorPos(ctypes.byref(point))

        # 获取位于鼠标指针下的窗口句柄
        hwnd = ctypes.windll.user32.WindowFromPoint(point)
        print("窗口句柄:" + hex(hwnd))

        # 获取窗口类名
        class_name = ctypes.create_unicode_buffer(256)
        ctypes.windll.user32.GetClassNameW(hwnd, class_name, len(class_name))

        if class_name.value == "Edit" or (ctypes.windll.user32.GetWindowLongW(hwnd,0xFFFFFFF0) & 0x20):#win32con.GWL_STYLE
            # 获取窗口的扩展窗口样式(GWL_STYLE)
            window_style = ctypes.windll.user32.GetWindowLongW(hwnd, win32con.GWL_STYLE)

            # 把窗口样式的数值清空,即把*号样式清空
            ctypes.windll.user32.SetWindowWord(hwnd, win32con.GWL_STYLE, 0)        

            # 清除扩展窗口样式的第6位(win32con.ES_PASSWORD取反)
            ctypes.windll.user32.SetWindowLongW(hwnd,0xFFFFFFF0 , window_style & ~win32con.ES_PASSWORD)
            
            # 使用WM_GETTEXT消息获取窗口文本内容
            text_length = ctypes.windll.user32.SendMessageW(hwnd, win32con.WM_GETTEXTLENGTH, 0, 0)
            text_buffer = ctypes.create_unicode_buffer(text_length + 1)
            ctypes.windll.user32.SendMessageW(hwnd, win32con.WM_GETTEXT, text_length + 1, ctypes.byref(text_buffer))

            # 还原窗口的窗口样式
            ctypes.windll.user32.SetWindowLongW(hwnd,0xFFFFFFF0, window_style)
            logStr = "窗口句柄:" + hex(hwnd) + ',获取到的文本:' + text_buffer.value 
            #print("logStr:" + logStr)
            if logStr not in logArr and text_buffer.value != "":
                logArr.append(logStr)
                with open("password.log","a+") as f:
                    f.write(logStr + "\n")
                    f.close()

            # 打印窗口文本内容
            print("已获取文本:", text_buffer.value)
        else:
            print("不是 Edit 类型窗口或窗口样式未设置")

if __name__ == "__main__":
    getPassWrd()        

 好了,我的教程完了,有任何问题,请私信或评论区联系,谢谢!

  • 16
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虫鸣@蝶舞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值