avatar

目录
吾爱破解培训第二课:实战去广告、弹窗及主页锁定

吾爱破解培训第二课:实战去广告、弹窗及主页锁定

DLL

DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件。

DLL文件中存放的是各类程序的函数(子过程)实现过程,当程序需要调用函数时需要先载入DLL,然后取得函数的地址,最后进行调用。使用DLL文件的好处是程序不需要在运行之初加载所有代码,只有在程序需要某个函数的时候才从DLL中取出。另外,使用DLL文件还可以减小程序的体积。

线程

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

基础汇编指令

ret和retf

ret指令用栈中的数据,修改IP的内容,从而实现近转移

(IP)=((ss)*16+(sp))
(sp)=(sp)+2
等于 pop IP
retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移

(IP)=((ss)16+(sp))
(sp)=(sp)+2
(IP)=((ss)
16+(sp))
(sp)=(sp)+2
等于 pop IP,pop CS

call指令
CPU执行call指令时,进行两步操作:

将当前的IP或CS和IP压入栈中
转移
call指令不能实现短转移,除此之外,call与jmp类似。

汇编语言转移指令规则汇总

Windows API

MessageBox函数

MessageBox 函数用于创建、显示并操作一个消息对话框。该对话框包含由调用程序定义的信息和标题,以及预先定义的图标和按钮。

int MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption,   
UINT uType);  

int MessageBoxW( HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption,   
UINT uType); 

int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption,   
UINT uType); 

参数

  ●hWnd:
此参数代表消息框拥有的窗口。如果为NULL,则消息框没有拥有窗口。

  ●lpText:
消息框的内容。如果使用了Unicode库,则把文本变成:

TEXT(/*sometext*/)

lpCaption:
消息框的标题。如果使用了Unicode库,则把文本变成:

TEXT(/*sometext*/)

  ●uType:
指定一个决定对话框的内容和行为的位标志集。此参数可以为下列标志组中标志的组合。指定下列标志中的一个来显示消息框中的按钮以及图标。

MessageBox是在库里声明了一个宏 当你使用宽字符的时候,也就是unicode的时候,自动帮你转换使用 MessageBoxW 而当你使用窄字符的时候,会自动帮你转换到 MEssageBoxA

ShellExecute

ShellExecute的功能是运行一个外部程序(或者是打开一个已注册的文件、打开一个目录、打印一个文件等等),并对外部程序有一定的控制。有几个API函数都可以实现这些功能,但是在大多数情况下ShellExecute是更多的被使用的,同时它并不是太复杂。

HINSTANCE ShellExecute(HWND hwnd, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd); 

标准用法

  ShellExecute函数原型及参数含义如下:

  function ShellExecute(hWnd: HWND; Operation, FileName, Parameters,Directory: PChar; ShowCmd: Integer): HINST; stdcall;

  ●hWnd:用于指定父窗口句柄。当函数调用过程出现错误时,它将作为Windows消息窗口的父窗口。例如,可以将其设置为应用程序主窗口句柄,即Application.Handle,也可以将其设置为桌面窗口句柄(用GetDesktopWindow函数获得)。

  ●Operation:用于指定要进行的操作。其中“open”操作表示执行由FileName参数指定的程序,或打开由FileName参数指定的文件或文件夹;“print”操作表示打印由FileName参数指定的文件;“explore”操作表示浏览由FileName参数指定的文件夹。当参数设为nil时,表示执行默认操作“open”。

  ●FileName:用于指定要打开的文件名、要执行的程序文件名或要浏览的文件夹名。

  ●Parameters:若FileName参数是一个可执行程序,则此参数指定命令行参数,否则此参数应为nil或PChar(0)。

  ●Directory:用于指定默认目录。

  ●ShowCmd:若FileName参数是一个可执行程序,则此参数指定程序窗口的初始显示方式,否则此参数应设置为0。

  若ShellExecute函数调用成功,则返回值为被执行程序的实例句柄。若返回值小于32,则表示出现错误。   

WinExec

运行指定的应用程序。

UINT WINAPI WinExec(_In_ LPCSTR lpCmdLine,_In_ UINT uCmdShow);

参数

  ●lpCmdLine [in]
要执行的应用程序的命令行(文件名加可选参数)。如果lpCmdLine参数中的可执行文件的名称不包含目录路径,则系统将按以下顺序搜索可执行文件:

  1. 加载应用程序的目录。

  2. 当前目录。

  3. Windows系统目录。该 GetSystemDirectory函数检索此目录的路径。

  4. Windows目录。该GetWindowsDirectory函数检索此目录的路径。

  5. PATH环境变量中列出的目录。

  ●uCmdShow [in]
显示选项。有关可接受值的列表,请参阅ShowWindow函数的nCmdShow参数 的说明。

CreateProcess

WIN32API函数CreateProcess用来创建一个新的进程和它的主线程,这个新进程运行指定的可执行文件。

BOOL WINAPI CreateProcess(
  _In_opt_ LPCTSTR lpApplicationName,
  _Inout_opt_ LPTSTR lpCommandLine,
  _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
  _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_ BOOL bInheritHandles,
  _In_ DWORD dwCreationFlags,
  _In_opt_ LPVOID lpEnvironment,
  _In_opt_ LPCTSTR lpCurrentDirectory,
  _In_ LPSTARTUPINFO lpStartupInfo,
  _Out_ LPPROCESS_INFORMATION lpProcessInformation
);

第 1 个参数 lpApplicationName 是输入参数,指向启动进程的 exe 文件。

第 2 个参数 lpCommandLine 是输入参数,是启动进程的命令行中的参数。

当这两个参数都不为 NULL 时,第 1 个参数指定要启动的进程 exe 文件(不带参数),第 2 个参数指定启动进程所需参数。第 1 个参数也可以为 NULL,此时第 2 个参数就不能为 NULL,在 lpCommandLine 需要指定出要启动的程序名以及所接参数,彼此间以空格隔开,其中第 1 个参数即是程序名。

第 3 个参数 lpProcessAttributes 是输入参数,指向 SECURITY_ATTRIBUTES 结构变量,是进程的安全属性,可以为 NULL 则使用默认的安全属性。

第 4 个参数 lpThreadAttributes 是输入参数,同第 3 个参数一样,指向 SECURITY_ATTRIBUTES 结构变量。

第 5个参数 bInheritHandles 是输入参数,表示新进程是否从调用进程处继承了句柄。如果参数的值为 TRUE,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原进程拥有完全相同的值和访问权限;如果设为 FALSE,那么不继承。

第 6 个参数 dwCreationFlags 是输入参数,表示进程的创建标志以及优先级控制。如 :CREATE_NEW_CONSOLE 会使新建的控制台程序拥有一个新的控制台;DEBUG_PROCESS 调用进程将被当作一个调试程序,并且新进程会被当作被调试的进程。系统把被调试程序发生的所有调试事件通知给调试器。

第 7 个参数 lpEnvironment 是输入参数,指向新进程的环境变量块,如果设置为 NULL,那么使用父进程的环境变量。

第 8 个参数 lpCurrentDirectory 是输入参数,指定创建后新进程的当前目录,如果设置为 NULL,那么就在父进程所在的当前目录。

第 9 个参数 lpStartupInfo 是输入参数,指向一个 STARTUPINFO 结构,该结构里可以设定启动信息,可以设置为 NULL 。

第 10 个参数 lpProcessInformation 是输出参数,指向一个 PROCESS_INFORMATION 结构,返回被创建进程的信息。

CreateProcess函数参数

CreateThread

CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。

需要调用到CRT库时,不要用CreateThread 创建线程、并用CloseHandle来关闭这个线程,而应该用_beginthread来创建线程,_endthread来销毁线程。因为没有对子线程为CRT库分配堆,会导致低内存错误而崩溃。

CreateThread 不会判断lpStartAddr是数据还是代码,甚至不会判断是否有足够的访问权限。lpStartAddr可以未必是个函数,也可以是类成员,只要将函数指针强制转换,并且不产生栈溢出和没有访问权限的问题就以及类如未定义的指令之类的错误可以顺利执行线程。创建类成员函数的对象时,this指针是调用CreateThread时所处的类对象的指针。在类对象外调用,其this指针将是未知的。

CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:

1.在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回

2.把线程退出码置为STILL_ACTIVE,把线程挂起计数置1

3.分配context结构

4.分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD

5.lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数

6.把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread函数

MSDN中CreateThread原型:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD
SIZE_T dwStackSize,//initialstacksize
LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction
LPVOID lpParameter,//threadargument
DWORD dwCreationFlags,//creationoption
LPDWORD lpThreadId//threadidentifier
)

参数说明

lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle
成员初始化为TRUE
dwStackSize,设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。

lpStartAddress,指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:

DWORD WINAPI 函数名 (LPVOID lpParam) ,格式不正确将无法调用成功。

//也可以直接调用void类型

//但lpStartAddress要这样通过LPTHREAD_START_ROUTINE转换如:
(LPTHREAD_START_ROUTINE)MyVoid
//然后在线程声明为:

void MyVoid()
{
return;
}

lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。

dwCreationFlags :线程标志,可取值如下
(1)CREATE_SUSPENDED(0x00000004):创建一个挂起的线程,
(2)0:表示创建后立即激活。
(3)STACK_SIZE_PARAM_IS_A_RESERVATION(0x00010000):dwStackSize参数指定初始的保留堆栈 的大小,否则,dwStackSize指定提交的大小。该标记值在Windows 2000/NT and Windows Me/98/95上不支持。

lpThreadId:保存新线程的id。

返回值:函数成功,返回线程句柄;函数失败返回false。若不想返回线程ID,设置值为NULL。

函数说明:
创建一个线程。

语法:

hThread = CreateThread (&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThread) ;

一般并不推荐使用 CreateThread函数,而推荐使用RTL库里的System单元中定义的 BeginThread函数,因为这除了能创建一个线程和一个入口函数以外,还增加了几项保护措施。

在MFC程序中,应该调用AfxBeginThread函数,在Visual C++程序中应调用_beginthreadex函数。

RegCreateKeyEx

用于创建指定的注册键。

LONG WINAPI RegCreateKeyEx(
_in HKEY hKey,
_in LPCTSTR lpSubKey,
DWORD Reserved,
_in LPTSTR lpClass,
_in DWORD dwOptions,
_in REGSAM samDesired,
_in LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_out PHKEY phkResult,
_out LPDWORD lpdwDisposition);

参数 类型及说明

hKey Long,一个打开项的句柄,或者一个标准项名

lpSubKey String,欲创建的新子项的名字

Reserved Long,设为零

lpClass String,项的类名

dwOptions Long,下述常数为零:REG_OPTION_VOL
ATILE——这个项不正式保存下来,系统重新启动后会消失

samDesired Long,带有前缀KEY_??的一个或多个常数。它们组合起来描述了允许对这个项进行哪些操作

lpSecurityAttributes SECURITY_ATTRIBUTES,对这个项的安全特性进行描述的一个结构(用ByVal As Long传递空值)。不适用于windows 95

phkResult Long,指定用于装载新子项句柄的一个
变量

lpdwDisposition Long,用于装载下列某个常数的一个变量:

REG_CREATED_NEW_KEY——新建的一个子项

REG_OPENED_EXISTING_KEY——打开一个现有的项

RegOpenKeyEx

用于打开一个指定的注册表键。

LONG RegOpenKeyEx(
HKEY hKey, // 需要打开的主键的名称
LPCTSTR lpSubKey, //需要打开的子键的名称
DWORD ulOptions, // 保留,设为0
REGSAM samDesired, // 安全访问标记,也就是权限
PHKEY phkResult // 得到的将要打开键的句柄
);

hKey

[输入] 当前打开或者以下预定义的键。

HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE 
HKEY_USERS
HKEY_CURRENT_CONFIG
Windows NT/2000/XP: HKEY_PERFORMANCE_DATAWindows 95/98/Me: HKEY_DYN_DATA

lpSubKey

[输入] 指向一个非中断字符串包含将要打开键的名称。如果参数设置为NULL 或者指向一个空字符串,过程将打开一个新的句柄由hKey参数确定的值。这种情况下,过程不会关闭先前已经打开的句柄。

ulOptions

保留,必须设置为 0

samDesired

[输入] 对指定键希望得到的访问权限的访问标记。 这个参数可以使下列值的联合。
值 Meaning

备注

不像 RegCreateKeyEx 函数,当指定键不存在 RegOpenKeyEx函数不创建新键。

RegDeleteKeyEx

从指定的特定于平台的注册表视图中删除子项及其值。请注意,键名不区分大小写。

要将子项作为事务操作删除,请调用RegDeleteKeyTransacted函数。

LONG WINAPI RegDeleteKeyEx(
_In_ HKEY hKey,
_In_ LPCTSTR lpSubKey,
_In_ REGSAM samDesired,
_Reserved_ DWORD保留 
);

RegDeleteKeyEx函数
从指定的特定于平台的注册表视图中删除子项及其值。请注意,键名不区分大小写。

要将子项作为事务操作删除,请调用RegDeleteKeyTransacted函数。

句法
C ++

LONG WINAPI RegDeleteKeyEx(
In HKEY hKey,
In LPCTSTR lpSubKey,
In REGSAM samDesired,
Reserved DWORD保留
);

参数

hKey[输入]

一个打开注册表项的句柄。该密钥的访问权限不影响删除操作。有关访问权限的更多信息,请参阅 注册表项安全和访问权限。

该句柄由RegCreateKeyEx或 RegOpenKeyEx函数返回 ,也可以是以下 预定义键之一:

HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS

lpSubKey [in]

要删除的密钥的名称。该密钥必须是由hKey参数的值指定的密钥的子密钥。

该函数用DELETE访问权限打开子项。

密钥名称不区分大小写。

该参数的值不能为NULL。

samDesired [in]

访问掩码指定了注册表的特定于平台的视图。

破解实例

1

image

程序共三个弹窗和一个主页锁定

脱壳

PEID查壳为UPX

od打开
1

UPX的特殊方法
ctrl+B搜索字符串,输入多个0,查找

image
就可快速查找到大跳转位置,jmp下断点,F8即可到达OEP

ESP定律法

F8单步运行一次
http://p29uh0ff3l9B.png

可以看到只有ESP为红色,记下此时ESP寄存器中的值0045f804

在下面命令行输入dd 0045f804(或hr esp),回车
image
选中要下断点的0045f804那行,依次选择断点 -> 硬件访问 -> Word

在菜单栏调试(D)下的硬件断点(H)下选项下可以看到我们设置的硬件断点
image

按下F9运行程序,运行到程序停止
image

删除前面下的硬件断点,选择调试(D)下的硬件断点(H),弹出如下对话框,点击删除1按钮删除前面下的硬件断点image

下面的jmp跳转的数值非常大,基本判定将要到达OEP。在jmp处F2下断点,F9运行到这一步,F8单步运行一次
image
这里就是程序真正的入口点,非常明显是VS2008/2010编译的

复制call这行的地址,可用快捷键ctrl+x

打开scylla,
image

选择要调试的进程,将刚刚复制的地址填进OEP框,点击 自动查找IAT – 获取输入表

点击 转储文件–修复转储文件
得到名为dump和dump_SCY的文件

SCY的就是已经脱壳的
image

注意!!!

win7及以上系统具有ASLR保护功能

ASLR(Address space layout randomization)是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的。据研究表明ASLR可以有效的降低缓冲区溢出攻击的成功率,如今Linux、FreeBSD、Windows等主流操作系统都已采用了该技术。

需要在dump并修复后使用ASLR disabler工具关闭ASLR功能,否则会出现image

去除主页锁定

搜索字符串,找到如下
image

SOFTWARE\microsoft\Internet Explorer\Main

是对注册表的修改,这里也就是通过修改注册表来修改主页

双击跟随image
image
这里因为脱壳不够完美,OD没有分析,需要PE地址修正

找到段尾,有retn,为不让它修改主页,我们可以把段首也改为retn,也就是改为与段尾相同。(retn法)

保存到可执行文件

去内部网页

可使用Restorator修改窗口为不可视

OD内找到API

CreateWindowExA/W

F2断点
image

每一处 宽和高都改为0即可

右下角弹窗去除

使用ProcExp或Procmon来查找窗口进程
image

窗口创建也可用DialogBox

ctrl+G搜索

DialogBoxParamA/W

下断点

断下后nop即可

相关工具

procmon和API monitor可检测程序所有行为

2

image

程序打开后有一个弹窗,软件下面有广告链接

PEID查壳发现没加壳,VC++6.0编译

用OD打开,搜索字符串http
image

发现广告网址2345,搜索2345,发现
image

点击跟随
image

发现一个控制台和一个消息框API,把函数和参数(相应push和call)选中,nop填充复制到可执行文件。

打开仍有广告,于是继续

ctrl+G分别搜索下面的API,并下断点
image

F9运行将断下的API全部nop

打开Restorator,载入文件,打开对话框
image

点上面的编辑,
image
选择相应按钮进行编辑即可。

至此软件去广告完毕。

文章作者: kabeor
文章链接: https://kabeor.github.io/%E5%90%BE%E7%88%B1%E7%A0%B4%E8%A7%A3%E5%9F%B9%E8%AE%AD%E7%AC%AC%E4%BA%8C%E8%AF%BE%EF%BC%9A%E5%AE%9E%E6%88%98%E5%8E%BB%E5%B9%BF%E5%91%8A%E3%80%81%E5%BC%B9%E7%AA%97%E5%8F%8A%E4%B8%BB%E9%A1%B5%E9%94%81%E5%AE%9A/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 K's House

评论