Application of API HOOK Technology in MFC Program Cracking

Update, modified the upload script of typora, and uploaded all the pictures to Kanxue

This article was published on my personal blog on 2023-08-02

The picture can't be loaded, put a PDF version in the attachment

There are a few pictures in the article are moving pictures, if you can’t move, you can go to my personal blog to see

I recently cracked an MFC program, OriginLab Pro , here is a record of the relevant process and some things I learned

Software download address , download the entire warehouse and then unzip it

locate activation code

After the program is installed, the activation interface will pop up when you open it.

image-20230731133459135

We choose to use the license for offline activation

image-20230731133626522

For this kind of input box, it must be used USER32!GetWindowText*to obtain user input in the end, just enter a string in the input box, and then use bm to set a wildcard breakpoint in windbg

1

2

3

4

5

6

7

0:005> bm user32!getwindowtext*

  100007ffb`a7f06f50 @!"USER32!GetWindowTextW$filt$0"

  200007ffb`a7f06e95 @!"USER32!GetWindowTextA$filt$0"

  300007ffb`a7ed9490 @!"USER32!GetWindowTextA"

  400007ffb`a7eda200 @!"USER32!GetWindowTextLengthW"

  500007ffb`a7edc2f0 @!"USER32!GetWindowTextW"

  600007ffb`a7f59ce0 @!"USER32!GetWindowTextLengthA"

After that click OK

The actual test found that before we click OK, these breakpoints will be triggered repeatedly, which will interfere with our finding the real license processing logic, so we need to improve it, first COMCTL32!Button_WndProc+0x7fbset Only then will it be triggered, and then click OK. After the breakpoint is triggered, click the wildcard breakpoint above to locate the real license processing logic.

image-20230731142s12806

Let's use IDA to look at the position numbered 02 in the call stack. First, this code is located inC:\Program Files\OriginLab\Origin2023b\ou.dll

Calculate the offset

1

2

3

4

5

6

7

8

9

10

11

12

0:000> lm m *ou*

Browse full module list

start             end                 module name

00000000`10000000 00000000`1046d000   Resource   (deferred)            

00007ffb`53b40000 00007ffb`54452000   SogouPy    (deferred)            

00007ffb`54460000 00007ffb`546a3000   sogoutsf   (deferred)            

00007ffb`54d90000 00007ffb`54de1000   OUIM       (deferred)            

00007ffb`76fb0000 00007ffb`76feb000   OCcontour   (deferred)            

00007ffb`79400000 00007ffb`794a4000   Outl       (deferred)            

00007ffb`794b0000 00007ffb`79be7000   ou         (export symbols)       C:\Program Files\OriginLab\Origin2023b\ou.dll

0:000> ? ou!COUxlView::xlWorksheetToNativeOrigin+0x23489-00007ffb`794b0000

Evaluate expression: 1271337 = 00000000`00136629

Jump to the corresponding location in IDA according to the offset

1

2

3

4

.text:000000018013661C                 lea     rdx, [rsp+38h+arg_18]

.text:0000000180136621                 mov     rcx, rbx

.text:0000000180136624                 call    ?GetWindowTextW@CWnd@@QEBAXAEAV?$CStringT@_WV?$StrTraitMFC_DLL@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@@Z ; CWnd::GetWindowTextW(ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>> &)

.text:0000000180136629                 lea     rcx, [rsp+38h+arg_18]

According to our experience, it can be seen at a glance that rdx is an outgoing parameter, which means that [rsp+38h+arg_18]the license we input will be saved

image-20230731142312806

Analyze assembly code

After looking at the function sub_180136600as a whole, we did not find the code related to activation, then we will continue to look at the code number 03 in the call stack, which is the functionsub_180134090

you!sub_180134090

The key code in this function is as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

.text:00000001801340AA                 lea     rcx, [rsp+28h+arg_8]

.text:00000001801340AF                 call    cs:??0CStringUTF8@@QEAA@XZ ; CStringUTF8::CStringUTF8(void)

.text:00000001801340B5                 nop

.text:00000001801340B6                 lea     rdx, [rdi+490h]

.text:00000001801340BD                 lea     r8, [rsp+28h+arg_8]

.text:00000001801340C2                 mov     rcx, rdi

.text:00000001801340C5                 call    sub_180136600

.text:00000001801340CA                 mov     rcx, [rsp+28h+arg_8]

.text:00000001801340CF                 call    sub_18012EF30

.text:00000001801340D4                 mov     ebx, eax

.text:00000001801340D6                 lea     rcx, [rsp+28h+arg_0]

.text:00000001801340DB                 call    cs:??0CStringUTF8@@QEAA@XZ ; CStringUTF8::CStringUTF8(void)

.text:00000001801340E1                 nop

.text:00000001801340E2                 mov     r9d, ebx

.text:00000001801340E5                 lea     r8, aBc04       ; "BC04:"

.text:00000001801340EC                 lea     rdx, aSD_2      ; "%s%d"

.text:00000001801340F3                 lea     rcx, [rsp+28h+arg_0]

.text:00000001801340F8                 call    cs:?Format@CStringUTF8@@QEAAXPEBDZZ ; CStringUTF8::Format(char const *,...)

.text:00000001801340FE                 mov     r8d, 1

.text:0000000180134104                 mov     rdx, [rsp+28h+arg_0]

.text:0000000180134109                 lea     ecx, [r8+14h]

.text:000000018013410D                 call    LABUTIL_diagnostics

.text:0000000180134112                 nop

.text:0000000180134113                 lea     rcx, [rsp+28h+arg_0]

.text:0000000180134118                 call    cs:__imp_??1CStringUTF8@@QEAA@XZ ; CStringUTF8::~CStringUTF8(void)

.text:000000018013411E                 mov     eax, cs:dword_1803D4898

.text:0000000180134124                 test    ebx, ebx

.text:0000000180134126                 cmovnz  eax, ebx

.text:0000000180134129                 mov     cs:dword_1803D4898, eax

.text:000000018013412F                 lea     rcx, [rsp+28h+arg_8]

.text:0000000180134134                 call    cs:__imp_??1CStringUTF8@@QEAA@XZ ; CStringUTF8::~CStringUTF8(void)

我们在ou+1340C5下断点,观察函数sub_180136600调用完成后第二个参数和第三个参数的情况

image-20230731145750931

很明显,第3个参数rsp+28h+arg_8是传出参数,调用完成后会保存我们的license字符串

继续观察这段代码我们可以发现,license text作为第1个参数传给了函数sub_18012EF30,返回值放到ebx中并最终作为函数CStringUTF8::Format的第4个参数,然后这个函数就结束了

那现在我们就进入函数sub_18012EF30中一探究竟

ou!sub_18012EF30

首先我们通过windbg的调试可以确定下面这个函数的两次调用分别返回了字符串REGIDFSN

image-20230731150429309

后面函数sub_180136A90也被调用了两次,我们先看第一次调用的参数传入情况

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

0:000> dc poi(rcx)

0000015f`eea4c9e8  73696874 20736920 6563696c 2065736e  this is license

0000015f`eea4c9f8  74786574 72003300 6e696769 5c62614c  text.3.riginLab\

0000015f`eea4ca08  6563694c 0065736e 00756a70 00adf00d  License.pju.....

0000015f`eea4ca18  eea4cb58 0000015f 0000002f baadf00d  X..._.../.......

0000015f`eea4ca28  65780035 646f4d64 6e496c65 2e626968  5.xedModelInhib.

0000015f`eea4ca38  00666466 61745320 64656b63 73694820  fdf. Stacked His

0000015f`eea4ca48  72676f00 6f2e6d61 00756a70 baadf00d  .ogram.opju.....

0000015f`eea4ca58  eea4ca18 0000015f 0000002f baadf00d  ...._.../.......

0:000> dc poi(rdx)

0000015f`fa7c2a08  49474552 20730044 6563696c 0065736e  REGID.s license.

0000015f`fa7c2a18  fa7c2978 0000015f 0000000f baadf00d  x)|._...........

0000015f`fa7c2a28  003d4c43 33323632 62302e00 005c6100  CL=.2623..0b.a\.

0000015f`fa7c2a38  fa7c2a18 0000015f 0000000f baadf00d  .*|._...........

0000015f`fa7c2a48  72747845 76650061 6c2e6c61 00006369  Extra.eval.lic..

0000015f`fa7c2a58  00000001 00000001 0000000f baadf00d  ................

0000015f`fa7c2a68  44500003 2e324345 00464446 baadf00d  ..PDEC2.FDF.....

0000015f`fa7c2a78  00000001 00000002 0000000f baadf00d  ................

0:000> dc poi(r8)

00007ffb`8a77f050  00000000 00000000 00000000 00000000  ................

00007ffb`8a77f060  8a77a8f8 00007ffb 00000020 00000fff  ..w..... .......

00007ffb`8a77f070  00015262 00000000 fd2fff10 0000015f  bR......../._...

00007ffb`8a77f080  fa7c3e78 0000015f 8a7721b0 00007ffb  x>|._....!w.....

00007ffb`8a77f090  8a7721c0 00007ffb ffffffff ffffffff  .!w.............

00007ffb`8a77f0a0  ffffffff 00000000 00000000 00000000  ................

00007ffb`8a77f0b0  00000000 00000000 020007d0 00000000  ................

00007ffb`8a77f0c0  8a77a8f8 00007ffb 00000040 00000fff  ..w.....@.......

0:000> r r9

r9=000000000000000b

  • 1p:我们输入的license text
  • 2p:REGID
  • 3p:传出参数
  • 4p:硬编码的值,0xB

ou!sub_180136A90

现在我们进入函数sub_180136A90,这个函数中调用的都是CString类的一些函数,因此很容易理清逻辑,这个函数会对我们的输入的license字符串作如下处理

  • license text转为大写
  • 获取REGID在license text中的index
  • 如果index为-1(license text中不存在REGID)或者0(REGID位于license text的开头),则返回1,同时传出参数为空
  • 否则从license text的index+len('REGID')+1的位置开始截取长度为0xB的字符串填充到传出参数,另外第一个参数,也就是我们输入的license text也发生了变化,index及之后的字符都被丢弃了

然后再次调用

image-20230731152625752

可以看到该函数第二次调用的返回值会直接影响到最终的返回值,0x191及401,也就是license text验证失败的错误代码

image-20230731152809407

很显然我们不希望最后返回401,因此我们要控制al不为0,那我们就可以构造出如下的license text来控制al为1

1

0FSN0123456789abcdefghijkREGIDABCDEFGHIJKLMNOPQRSTUVWXYZ

然后函数sub_180136A90两次调用的传出参数将会分别作为函数okuCountTotalSeries的参数被调用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

0:000> dc rcx

0000015f`fa7c2a28  45444342 49484746 004c4b4a 005c6174  BCDEFGHIJKL.ta\.

0000015f`fa7c2a38  fa7c2958 0000015f 0000000f baadf00d  X)|._...........

0000015f`fa7c2a48  003d4c43 33323632 62302e00 005c6100  CL=.2623..0b.a\.

0000015f`fa7c2a58  00000001 00000001 0000000f baadf00d  ................

0000015f`fa7c2a68  44500003 2e324345 00464446 baadf00d  ..PDEC2.FDF.....

0000015f`fa7c2a78  00000001 00000002 0000000f baadf00d  ................

0000015f`fa7c2a88  44004303 2e334345 00464446 baadf00d  .C.DEC3.FDF.....

0000015f`fa7c2a98  00000001 00000001 0000000f baadf00d  ................

0:000> dc rdx

0000015f`eea4c868  34333231 38373635 63626139 67666564  123456789abcdefg

0000015f`eea4c878  69006968 62614c6e 6369005c 65736e65  hi.inLab\.icense

0000015f`eea4c888  51000000 55545352 59585756 0000005a  ...QRSTUVWXYZ...

0000015f`eea4c898  eea4cb98 0000015f 0000002f baadf00d  ...._.../.......

0000015f`eea4c8a8  4e534630 33323130 37363534 62613938  0FSN0123456789ab

0000015f`eea4c8b8  66656463 6a696867 6369006b 65736e65  cdefghijk.icense

0000015f`eea4c8c8  65630000 0065736e 36323532 00003332  ..cense.252623..

0000015f`eea4c8d8  00000001 00000013 0000002f baadf00d  ......../.......

ok!okuCountTotalSeries

该函数位于C:\Program Files\OriginLab\Origin2023b\ok.dll

image-20230731162804040

从上图中可以看出,我们需要控制函数okuCountTotalSeries的返回值不为0

那么我们就需要控制该函数内部的这两个分支,不能跳到loc_180E0C1D7

image-20230731162949620

ok!sub_180E0A430

该函数代码不多,逻辑也是相当的简单

image-20230731163332758

就是把我们传给函数okuCountTotalSeries的第2个参数,也就是字符串

1

2

123456789abcdefghi

012345678901234567

的0xB处开始拷贝出来,就是cdefghi,返回值就是atol函数的返回值,很显然,当前的注册码是没有办法转换成long的,因此我们需要把cdefghi改成1234567

现在我们的注册码就变成了

1

0FSN0123456789ab1234567jkREGIDABCDEFGHIJKLMNOPQRSTUVWXYZ

ok!sub_180DF8F70

这个函数的内容有一点长,其传入的参数如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

0:000> dc rcx

0000015f`fa7c3e88  45444342 49484746 004c4b4a 00006369  BCDEFGHIJKL.ic..

0000015f`fa7c3e98  00000001 00000001 0000000f baadf00d  ................

0000015f`fa7c3ea8  6d650030 005c7365 00332e32 00007300  0.emes\.2.3..s..

0000015f`fa7c3eb8  00000001 00000009 0000000f baadf00d  ................

0000015f`fa7c3ec8  57746547 6449646e 746e0078 006c6f72  GetWndIdx.ntrol.

0000015f`fa7c3ed8  00000001 0000000f 0000000f baadf00d  ................

0000015f`fa7c3ee8  4e746547 65657254 6e616843 00736567  GetNTreeChanges.

0000015f`fa7c3ef8  00000001 0000000d 0000000f baadf00d  ................

0:000> r rdx

rdx=0000000000000000

0:000> r r8

r8=0000000000000000

我不想进去看了,直接看一下返回值,返回值是一个整型数,然后和函数sub_180E0A430的返回值进行了比较,如果两者不相等,最终就会返回0

那么很简单,我们只需要控制sub_180E0A430的返回值和该函数的返回值一样即可,当前函数的返回值是0x42dded,即4382189,再次更新我们的注册码

 

1

0FSN0123456789ab4382189jkREGIDABCDEFGHIJKLMNOPQRSTUVWXYZ

好了,现在我们重新回到ou.dll的函数sub_18012EF30

ou!loc_18012EFDC

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

mov     dl, 22h '"'

lea     rcx, [rbp+arg_10]

call    cs:?ReverseFind@CStringUTF8@@QEBAHD@Z ; CStringUTF8::ReverseFind(char)

lea     r8d, [rax+1]

lea     rdx, [rbp+var_18]

lea     rcx, [rbp+arg_10]

call    cs:?Left@CStringUTF8@@QEBA?AV1@H@Z ; CStringUTF8::Left(int)

nop

mov     rax, [rbp+var_20]

mov     [rsp+50h+var_30], rax

mov     r9, [rbp+arg_18]

xor     r8d, r8d

lea     rdx, [rbp+arg_8]

lea     rcx, [rbp+var_18]

call    sub_1801308E0

mov     ecx, 191h

test    eax, eax

cmovz   edi, ecx

lea     rcx, [rbp+var_18]

call    cs:__imp_??1CStringUTF8@@QEAA@XZ ; CStringUTF8::~CStringUTF8(void)

jmp     short loc_18012F033

上面汇编代码中的[rbp+arg_10]就是函数sub_180136A90的第1个参数,我们前面已经知道了该函数会改变自己第一个参数的内容,就是会把REGID和FSN及后面的字符都丢掉,那么到这里[rbp+arg_10]的值就是

1

0

这段代码做的事就是在[rbp+arg_10]找到"最后一次出现的位置,然后丢弃该位置之后的字符

最后调用函数sub_1801308E0

ou!sub_1801308E0

我们先看一下参数传入情况

  • 1p:前面使用"截取出来的字符串
  • 2p:传出参数
  • 3p:0
  • 4p:123456789ab4382189
  • 5p:BCDEFGHIJKL

该函数会检查第一个参数是否是空字符串,因为我们没有",所以第一道检查就挂了

另外就是该函数的返回值也需要进行控制,根据loc_18012EFDC中的内容

1

2

3

mov     ecx, 191h

test    eax, eax

cmovz   edi, ecx

只有eax不为0,edi才不会变成0x191

因此我们需要经过该函数的重重检查,到达loc_180130A1F

image-20230731171634145

前面的检查都由CString类的函数完成,分析起来很简单,这里不再赘述,最终形成的注册码为:

1

INCREMENTFEATUREorglabSIGN123456789""FSN09876543278L5824771TUVWXYZREGIDFSNABCDEFGHIJKLMNOPQRSTUVWXYZ

但是输入该注册码之后,并没有提示我们注册成功,虽然没有401报错了,但是我们的软件仍未被激活

仔细审查函数sub_1801308E0的代码,可以在函数尾部发现有一个叫做COKAccess::GetTempViewportLimits的函数被调用,其第1个参数为

1

INCREMENTFEATUREorglabSIGN123456789""

进入该函数,一路跟到了ok.dllsub_18012DDA0

ok!sub_18012DDA0

image-20230731174604691

在该函数下断点,我们可以观察到,注册码中的下面三部分被存储到了内存的特定位置中

1

2

3

INCREMENTFEATUREorglabSIGN123456789""

9876543278L5824771

SNABCDEFGHI

我们记录下这三个字符串存储的内存地址,然后下内存读的条件断点

通过内存访问断点定位检测代码

我直接在这三个内存地址上下内存读断点

1

2

3

ba r1 00000218`db5dab88

ba r1 00000218`db5dac08

ba r1 00000218`dc168aa8

最后在第二个断点被触发时得到如下调用栈

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

Breakpoint 1 hit

ok!OSetNumericSettings::operator=+0x403b:

00007ffc`178eec1b 0f95c0          setne   al

0:000> k

 # Child-SP          RetAddr               Call Site

00 000000ad`bfdff558 00007ffc`178ec37b     ok!OSetNumericSettings::operator=+0x403b

01 000000ad`bfdff560 00007ffc`17f4eae3     ok!OSetNumericSettings::operator=+0x179b

02 000000ad`bfdff5a0 00007ffc`17f54649     ok!okfxDoNewLegendEntries+0x21b3

03 000000ad`bfdff5e0 00007ffc`17f54f6d     ok!GetObjectPlotCategory::ObjectSeriesSetType+0x1229

04 000000ad`bfdff630 00007ffc`17f50673     ok!GetObjectPlotCategory::ObjectSeriesSetType+0x1b4d

05 000000ad`bfdffcb0 00007ffc`17f535c1     ok!GetObjectPlotCategory::DataSeriesGetProcessedData+0xe93

06 000000ad`bfdffce0 00007ffc`17955ded     ok!GetObjectPlotCategory::ObjectSeriesSetType+0x1a1

07 000000ad`bfdffd30 00007ffc`1a962731     ok!COKAccess::OkOnItemReDraw+0x10d

08 000000ad`bfdffdb0 00007ffc`33c70a7c     ou!COriginApp::OnIdle+0xd1

09 000000ad`bfdffe00 00007ffc`33ca3c20     mfc140u!CWinThread::Run+0x5c [D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\thrdcore.cpp @ 621]

0a 000000ad`bfdffe40 00007ff6`625ea44e     mfc140u!AfxWinMain+0xc0 [D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\winmain.cpp @ 61]

0b 000000ad`bfdffe80 00007ffc`651a7614     Origin64+0xa44e

0c 000000ad`bfdffec0 00007ffc`654a26b1     KERNEL32!BaseThreadInitThunk+0x14

0d 000000ad`bfdffef0 00000000`00000000     ntdll!RtlUserThreadStart+0x21

稍加整理,即可得到函数的调用顺序,我们只取出调用栈中的前几个即可,整理出来如下的调用栈

1

2

3

4

5

6

7

sub_180793590

    -> sub_180790520   调用点:0x18079066E

        -> sub_180794B10

            -> sub_180794600

                -> sub_18078EAC0

                    -> sub_18012C360

                        -> sub_18012EC10

这里面的函数代码一个比一个长,简单放一个拓扑图感受一下

image-20230801012233407

所以这些函数我都是大致浏览了一下,其中函数sub_180790520吸引到了我的注意

在下面这个地方(0x18079066E),函数调用了sub_180794B10,而这个函数的返回值决定了分支的走向

image-20230801012617114

继续往下浏览,我发现在右边的分支最终会调用函数COKAccess::UpdateMainWinTitle,这个看起来很不错,因为如果是激活失败的话也没必要更新主窗口的标题,可以看到我当前的测试软件的窗口标题中有一个Expired,同时左边的分支并没有什么有趣的东西,因此我们尝试把函数sub_180794B10的返回值强制修改为1

image-20230801012947437

patch程序

这里之所以选择修改返回值而不是分析函数sub_180794B10的代码,是因为这个函数实在是太复杂了,不如赌一把直接修改返回值

看一下缩略图就知道这个函数有多复杂了,比我上面贴的那个还复杂

image-20230801015555174

想要把这个函数分析明白需要很多时间,所以我选择直接patch

我们最终会从左边那个分支返回,所以我们需要把mov eax, ebx修改掉,保证最后的返回值非0

image-20230801015820452

这个指令对应的机器码如下

1

2

3

0:001> u ok+7954CB

ok!GetObjectPlotCategory::ObjectSeriesSetType+0x20ab:

00007ffc`17f554cb 8bc3            mov     eax,ebx

只占用2个字节,因此我们需要搞一个只占用2字节,而且还能保证eax非0的指令

image-20230801020330001

如上图所示,mov al, 1就正好符合我们的需求,我们只需要使用管理员权限打开IDA,将ok.dll的0x1807954CB修改为b001即可

image-202308010228518a71

 

使用API HOOK技术定位消息发送代码

我当时以为这把肯定能搞定了,结果输入注册码之后弹出了如下消息框,真的是太难了

image-20230801022851871

现在我要找一下这个窗是怎么弹出来的,在user32!messageboxw下断点,看一下调用栈

1

2

3

4

5

6

7

Breakpoint 0 hit

USER32!MessageBoxW:

00007ffc`64609750 4883ec38        sub     rsp,38h

0:000> k

 # Child-SP          RetAddr               Call Site

00 00000046`963fe638 00007ffc`1a939814     USER32!MessageBoxW

01 00000046`963fe640 00007ffc`33c8782e     ou!CMainFrame::WindowProc+0x704

这个消息框是在CMainFrame::WindowProc函数的0x18003980F位置被调用的

这个函数看名字再加上IDA分析出来的参数情况,基本上能猜出来是通过消息触发的,后两个参数应该就是lParam和wParam,和SendMessage的参数对应

image-20230801025717941

我们可以使用IDA的分支图追溯一下函数MessageBoxW的第2和第3个参数(就是消息框的标题和内容)是哪里来的

imasge-20230801025717941

最终可以定位到这两个参数是在0x18003940F 0x180039422被初始化的,从这个地方的函数名称和参数传入情况就能看出来,第一个参数是传出参数,将会保存一个字符串,并且这个函数两次调用,其第2个参数分别是CMainFrame::WindowProc的第4和第3个参数

因此我们可以下下面这样的断点来观察该函数调用前后的参数情况

1

2

3

ba e1 ou+3940F "r rdx;g"

ba e1 ou+39422 "dc poi(rsp+48);r rdx;g"

ba e1 ou+39428 "dc poi(rsp+40);g"

得到如下结果:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

rdx=0000000000003f0e

000001d9`d2b91dc8  00740041 00650074 0074006e 006f0069  A.t.t.e.n.t.i.o.

000001d9`d2b91dd8  0021006e abab0000 abababab abababab  n.!.............

000001d9`d2b91de8  abababab feeeabab 00000000 00000000  ................

000001d9`d2b91df8  00000000 00000000 feeefeee feeefeee  ................

000001d9`d2b91e08  bd768892 00fbed99 d2a8e380 000001d9  ..v.............

000001d9`d2b91e18  c305e490 000001d9 feeefeee feeefeee  ................

000001d9`d2b91e28  feeefeee feeefeee feeefeee feeefeee  ................

000001d9`d2b91e38  feeefeee feeefeee feeefeee feeefeee  ................

rdx=0000000000003fb6

000001d9`d2a8e398  006f0059 00720075 00460020 0045004c  Y.o.u.r. .F.L.E.

000001d9`d2a8e3a8  006c0058 0020006d 0069006c 00650063  X.l.m. .l.i.c.e.

000001d9`d2a8e3b8  0073006e 00200065 00690066 0065006c  n.s.e. .f.i.l.e.

000001d9`d2a8e3c8  00690020 00200073 006e0069 00610076   .i.s. .i.n.v.a.

000001d9`d2a8e3d8  0069006c 002e0064 abab0000 abababab  l.i.d...........

000001d9`d2a8e3e8  abababab abababab feeeabab feeefeee  ................

000001d9`d2a8e3f8  feeefeee feeefeee 00000000 00000000  ................

000001d9`d2a8e408  00000000 00000000 feeefeee feeefeee  ................

可以看到两次调用的第2个参数分别为0x3f0e0x3fb6,我们选择0x3fb6作为过滤条件,因为标题区分度不够高,可能别的窗口也会使用同样的标题,而内容重复的概率不高,那么我们就可以HOOK住消息发送函数,检测传进来的wParam是否为0x3fb6,消息发送函数我知道的一共有两个,一个是SendMessage,另一个是PostMessage,我当时先HOOK的SendMessageW,但是并没有拦截到wParam值为0x3fb6的调用,后面我又HOOK了PostMessageW,成功拦截,下面我记录一下我HOOK这两个API的过程

api hook的技术细节

我在网上搜了搜,给出的方案是把原始函数的前面几个字节替换成跳转到我们的hook函数的指令,然后在hook函数中对参数进行过滤,之后恢复原始函数的前面几个字节,再去调用原始函数并返回

但是这个并不满足我的需求,由于在hook函数内修复了原始函数,所以他只能hook一次

The way I thought about it later was to fix the original function in the hook function, save the return value after calling the original function, then modify the first few bytes of the original function, hook the function again, and then return the previously saved return value, but this There will be problems in multi-threading. Finally, under the reminder of Brother Wang , I found the following hook method

image-20230801200025544

The final result is that when PostMessageWcalled, the direction of the instruction becomes as follows

image-20230801200204285

The principle is clear, and the code is easy to write. As shown in the figure below, we have successfully located the call stack that sent the message

image-20230801213826730

This message is actually sub_180793590sent by the call location at0x180793625

And this function is called at OkOnItemReDrawthe 0x180195DE8 location, observe the branch direction of the function and combine the analysis of each function name called in it to locate the position of the key judgment statement:0x180195D3E

1

2

test    eax, eax

jnz     loc_18019624F

Repeat the same trick, just jnzchange it to , the machine code of the former is , and the machine code of the latter is . The registration information is still inactive, but it does not affect normal usejz0F 850F 84

OK, let’s stop here for this cracking, I still hope that the masters will point out the shortcomings

The tools used in this article and the DLL after patch are packaged here

Guess you like

Origin blog.csdn.net/q2919761440/article/details/132216488